from itertools import chain
from weakref import WeakSet
import operator
class Dynamic:
@classmethod
def args(self, *args, **kw):
return lambda f: Dynamic(f, *args, **kw)
def __init__(self, f, *args, **kw):
self._ismodified = True
self._cache = None
self.thunk = lambda: None, (), {}
self.children = WeakSet() # тут надо немного посидеть с дебагером
self.parents = set() # и убедиться что нет утечек памяти
self.update(f, *args, **kw)
def update(self, f, *args, **kw):
try:
thunk = f.thunk
parents = set(f.parents)
except AttributeError:
thunk = f, args, kw
parents = set(chain(args, kw.values()))
if self in parents: # возможно это можно обработать лучше
parents.discard(self)
oldself = Dynamic(self) # fork self
f, args, kw = thunk
args = list(args)
for i, a in enumerate(args): # patch thunk
if a is self:
args[i] = oldself
for k, v in kw.items():
if v is self:
kw[k] = oldself
thunk = f, tuple(args), kw
parents.add(oldself)
for p in parents:
p.children.add(self)
for p in self.parents:
p.children.discard(self)
self.thunk = thunk
self.parents = parents
self.setmodified()
def setmodified(self):
if not self._ismodified:
self._ismodified = True
for c in self.children:
c.setmodified()
def compose(self, f, d):
return Dynamic(f, self, d)
def __get__(self, obj, type=None):
return self
def __set__(self, obj, d):
self.update(d)
def __call__(self):
if self._ismodified:
self._ismodified = False
f, args, kw = self.thunk
self._cache = f(
*[d() for d in args],
**{k: v() for k, v in kw}
)
return self._cache
def __hash__(self):
return id(self)
def __repr__(self):
return 'Dynamic({}:{})'.format(hash(self), repr(self()))
def __str__(self):
return str(self())
def __add__(self, d):
return self.compose(operator.add, d)
def __mul__(self, d):
return self.compose(operator.mul, d)
def __truediv__(self, d):
return self.compose(operator.div, d)
def __iadd__(self, d):
self.update(self + d)
return self
def __imul__(self, d):
self.update(self * d)
return self
def __itruediv__(self, d):
self.update(self / d)
return self
# и т.д...
# это всё конечно можно сгенерировать на этапе создания экземпляра класса,
# но мне влом.
x = Dynamic(lambda: 2)
a = Dynamic(lambda: 5)
b = Dynamic(lambda: 2)
c = a * b
print('c:', c)
a += x
print('c:', c)
x.update(lambda: 0)
print('c:', c)
@Dynamic.args(c, b)
def d(c, b):
return c / b
print('d:', d)
class Table:
q = Dynamic(lambda: 9)
w = Dynamic(lambda: 8)
e = q + w
z = Table()
print('z.e', z.e)
z.q = Dynamic(lambda: 0)
print('z.e', z.e)
ZnJvbSBpdGVydG9vbHMgaW1wb3J0IGNoYWluCmZyb20gd2Vha3JlZiBpbXBvcnQgV2Vha1NldAppbXBvcnQgb3BlcmF0b3IKCmNsYXNzIER5bmFtaWM6CiAgICBAY2xhc3NtZXRob2QKICAgIGRlZiBhcmdzKHNlbGYsICphcmdzLCAqKmt3KToKICAgICAgICByZXR1cm4gbGFtYmRhIGY6IER5bmFtaWMoZiwgKmFyZ3MsICoqa3cpCiAgICAgICAgCiAgICBkZWYgX19pbml0X18oc2VsZiwgZiwgKmFyZ3MsICoqa3cpOgogICAgICAgIHNlbGYuX2lzbW9kaWZpZWQgPSBUcnVlCiAgICAgICAgc2VsZi5fY2FjaGUgPSBOb25lCiAgICAgICAgCiAgICAgICAgc2VsZi50aHVuayA9IGxhbWJkYTogTm9uZSwgKCksIHt9CiAgICAgICAgc2VsZi5jaGlsZHJlbiA9IFdlYWtTZXQoKSAgIyDRgtGD0YIg0L3QsNC00L4g0L3QtdC80L3QvtCz0L4g0L/QvtGB0LjQtNC10YLRjCDRgSDQtNC10LHQsNCz0LXRgNC+0LwKICAgICAgICBzZWxmLnBhcmVudHMgPSBzZXQoKSAgICAgICAjINC4INGD0LHQtdC00LjRgtGM0YHRjyDRh9GC0L4g0L3QtdGCINGD0YLQtdGH0LXQuiDQv9Cw0LzRj9GC0LgKICAgICAgICAKICAgICAgICBzZWxmLnVwZGF0ZShmLCAqYXJncywgKiprdykKICAgIAogICAgZGVmIHVwZGF0ZShzZWxmLCBmLCAqYXJncywgKiprdyk6CiAgICAgICAgdHJ5OgogICAgICAgICAgICB0aHVuayA9IGYudGh1bmsKICAgICAgICAgICAgcGFyZW50cyA9IHNldChmLnBhcmVudHMpCiAgICAgICAgCiAgICAgICAgZXhjZXB0IEF0dHJpYnV0ZUVycm9yOgogICAgICAgICAgICB0aHVuayA9IGYsIGFyZ3MsIGt3CiAgICAgICAgICAgIHBhcmVudHMgPSBzZXQoY2hhaW4oYXJncywga3cudmFsdWVzKCkpKQogICAgICAgIAogICAgICAgIGlmIHNlbGYgaW4gcGFyZW50czogICAgICAgICAgICAgICMg0LLQvtC30LzQvtC20L3QviDRjdGC0L4g0LzQvtC20L3QviDQvtCx0YDQsNCx0L7RgtCw0YLRjCDQu9GD0YfRiNC1CiAgICAgICAgICAgIHBhcmVudHMuZGlzY2FyZChzZWxmKQogICAgICAgICAgICBvbGRzZWxmID0gRHluYW1pYyhzZWxmKSAgICAgICMgZm9yayBzZWxmCiAgICAgICAgICAgIAogICAgICAgICAgICBmLCBhcmdzLCBrdyA9IHRodW5rCiAgICAgICAgICAgIGFyZ3MgPSBsaXN0KGFyZ3MpCiAgICAgICAgICAgIAogICAgICAgICAgICBmb3IgaSwgYSBpbiBlbnVtZXJhdGUoYXJncyk6ICAjIHBhdGNoIHRodW5rCiAgICAgICAgICAgICAgICBpZiBhIGlzIHNlbGY6CiAgICAgICAgICAgICAgICAgICAgYXJnc1tpXSA9IG9sZHNlbGYKICAgICAgICAgICAgCiAgICAgICAgICAgIGZvciBrLCB2IGluIGt3Lml0ZW1zKCk6CiAgICAgICAgICAgICAgICBpZiB2IGlzIHNlbGY6CiAgICAgICAgICAgICAgICAgICAga3dba10gPSBvbGRzZWxmCiAgICAgICAgICAgIAogICAgICAgICAgICB0aHVuayA9IGYsIHR1cGxlKGFyZ3MpLCBrdwogICAgICAgICAgICBwYXJlbnRzLmFkZChvbGRzZWxmKQogICAgICAgIAogICAgICAgIGZvciBwIGluIHBhcmVudHM6CiAgICAgICAgICAgIHAuY2hpbGRyZW4uYWRkKHNlbGYpCiAgICAgICAgCiAgICAgICAgZm9yIHAgaW4gc2VsZi5wYXJlbnRzOgogICAgICAgICAgICBwLmNoaWxkcmVuLmRpc2NhcmQoc2VsZikKICAgICAgICAKICAgICAgICBzZWxmLnRodW5rID0gdGh1bmsKICAgICAgICBzZWxmLnBhcmVudHMgPSBwYXJlbnRzCiAgICAgICAgc2VsZi5zZXRtb2RpZmllZCgpCiAgICAKICAgIGRlZiBzZXRtb2RpZmllZChzZWxmKToKICAgICAgICBpZiBub3Qgc2VsZi5faXNtb2RpZmllZDoKICAgICAgICAgICAgc2VsZi5faXNtb2RpZmllZCA9IFRydWUKICAgICAgICAKICAgICAgICAgICAgZm9yIGMgaW4gc2VsZi5jaGlsZHJlbjoKICAgICAgICAgICAgICAgIGMuc2V0bW9kaWZpZWQoKQogICAgCiAgICBkZWYgY29tcG9zZShzZWxmLCBmLCBkKToKICAgICAgICByZXR1cm4gRHluYW1pYyhmLCBzZWxmLCBkKQogICAgCiAgICBkZWYgX19nZXRfXyhzZWxmLCBvYmosIHR5cGU9Tm9uZSk6CiAgICAgICAgcmV0dXJuIHNlbGYKICAgIAogICAgZGVmIF9fc2V0X18oc2VsZiwgb2JqLCBkKToKICAgICAgICBzZWxmLnVwZGF0ZShkKQogICAgCiAgICBkZWYgX19jYWxsX18oc2VsZik6CiAgICAgICAgaWYgc2VsZi5faXNtb2RpZmllZDoKICAgICAgICAgICAgc2VsZi5faXNtb2RpZmllZCA9IEZhbHNlCiAgICAgICAgICAgIGYsIGFyZ3MsIGt3ID0gc2VsZi50aHVuawogICAgICAgICAgICAKICAgICAgICAgICAgc2VsZi5fY2FjaGUgPSBmKAogICAgICAgICAgICAgICAgKltkKCkgZm9yIGQgaW4gYXJnc10sCiAgICAgICAgICAgICAgICAqKntrOiB2KCkgZm9yIGssIHYgaW4ga3d9CiAgICAgICAgICAgICAgICApCiAgICAgICAgCiAgICAgICAgcmV0dXJuIHNlbGYuX2NhY2hlCiAgICAKICAgIGRlZiBfX2hhc2hfXyhzZWxmKToKICAgICAgICByZXR1cm4gaWQoc2VsZikKICAgIAogICAgZGVmIF9fcmVwcl9fKHNlbGYpOgogICAgICAgIHJldHVybiAnRHluYW1pYyh7fTp7fSknLmZvcm1hdChoYXNoKHNlbGYpLCByZXByKHNlbGYoKSkpCiAgICAKICAgIGRlZiBfX3N0cl9fKHNlbGYpOgogICAgICAgIHJldHVybiBzdHIoc2VsZigpKQogICAgCiAgICBkZWYgX19hZGRfXyhzZWxmLCBkKToKICAgICAgICByZXR1cm4gc2VsZi5jb21wb3NlKG9wZXJhdG9yLmFkZCwgZCkKICAgIAogICAgZGVmIF9fbXVsX18oc2VsZiwgZCk6CiAgICAgICAgcmV0dXJuIHNlbGYuY29tcG9zZShvcGVyYXRvci5tdWwsIGQpCiAgICAKICAgIGRlZiBfX3RydWVkaXZfXyhzZWxmLCBkKToKICAgICAgICByZXR1cm4gc2VsZi5jb21wb3NlKG9wZXJhdG9yLmRpdiwgZCkKICAgIAogICAgZGVmIF9faWFkZF9fKHNlbGYsIGQpOgogICAgICAgIHNlbGYudXBkYXRlKHNlbGYgKyBkKQogICAgICAgIHJldHVybiBzZWxmCiAgICAKICAgIGRlZiBfX2ltdWxfXyhzZWxmLCBkKToKICAgICAgICBzZWxmLnVwZGF0ZShzZWxmICogZCkKICAgICAgICByZXR1cm4gc2VsZgogICAgCiAgICBkZWYgX19pdHJ1ZWRpdl9fKHNlbGYsIGQpOgogICAgICAgIHNlbGYudXBkYXRlKHNlbGYgLyBkKQogICAgICAgIHJldHVybiBzZWxmCiAgICAjINC4INGCLtC0Li4uCiAgICAjINGN0YLQviDQstGB0ZEg0LrQvtC90LXRh9C90L4g0LzQvtC20L3QviDRgdCz0LXQvdC10YDQuNGA0L7QstCw0YLRjCDQvdCwINGN0YLQsNC/0LUg0YHQvtC30LTQsNC90LjRjyDRjdC60LfQtdC80L/Qu9GP0YDQsCDQutC70LDRgdGB0LAsCiAgICAjINC90L4g0LzQvdC1INCy0LvQvtC8LgoKeCA9IER5bmFtaWMobGFtYmRhOiAyKQphID0gRHluYW1pYyhsYW1iZGE6IDUpCmIgPSBEeW5hbWljKGxhbWJkYTogMikKCmMgPSBhICogYgpwcmludCgnYzonLCBjKQoKYSArPSB4CnByaW50KCdjOicsIGMpCgp4LnVwZGF0ZShsYW1iZGE6IDApCnByaW50KCdjOicsIGMpCgpARHluYW1pYy5hcmdzKGMsIGIpCmRlZiBkKGMsIGIpOgogICAgcmV0dXJuIGMgLyBiCgpwcmludCgnZDonLCBkKQoKY2xhc3MgVGFibGU6CiAgICBxID0gRHluYW1pYyhsYW1iZGE6IDkpCiAgICB3ID0gRHluYW1pYyhsYW1iZGE6IDgpCiAgICBlID0gcSArIHcKCnogPSBUYWJsZSgpCnByaW50KCd6LmUnLCB6LmUpCgp6LnEgPSBEeW5hbWljKGxhbWJkYTogMCkKcHJpbnQoJ3ouZScsIHouZSkK