Skip to content

Instantly share code, notes, and snippets.

@fre-sch
Created May 24, 2019 21:46
Show Gist options
  • Save fre-sch/71a5c56363a30d6ebc53d6f991b8685b to your computer and use it in GitHub Desktop.
Save fre-sch/71a5c56363a30d6ebc53d6f991b8685b to your computer and use it in GitHub Desktop.
from collections import defaultdict
class ObservableAttr:
class _unset: pass
def __init__(self, default=_unset):
self.default = default
self.name = "unnamed"
def __set_name__(self, cls, name):
self.name = name
def __set__(self, inst, value):
try:
inst_attrs = inst.__observable_attrs
except AttributeError:
inst_attrs = inst.__observable_attrs = {}
inst_attrs[self.name] = value
try:
listeners = inst.__observable_attrs_listener
except AttributeError:
listeners = []
for listener in listeners[self.name]:
listener(inst, self.name, value)
def __get__(self, inst, cls):
if inst is None:
return self
value = inst.__observable_attrs.get(self.name, self.default)
if value is ObservableAttr._unset:
raise AttributeError(self.name)
return value
def __repr__(self):
return f"ObservableAttr({self.name})"
@classmethod
def observe(cls, inst, attr, listener):
try:
listeners = inst.__observable_attrs_listener
except AttributeError:
listeners = inst.__observable_attrs_listener = defaultdict(list)
listeners[attr].append(listener)
class Model:
foo = ObservableAttr("default foo")
bar = ObservableAttr("default bar")
def listener(inst, attr, value):
print(f"{attr} on {inst} set to {value}")
model = Model()
ObservableAttr.observe(model, "foo", listener)
ObservableAttr.observe(model, "bar", listener)
model.foo = "new foo value"
model.bar = "new bar value"
print(f"model.foo {model.foo}")
print(f"model.bar {model.bar}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment