property question

M

Manu Hack

hi all,

If I have a class A with A.x, A.y, A.z. A.y and A.z are property and
in order to compute the value of them, A.y depends on A.x while A.z
depends on A.y and A.x. If I call A.y, and A.z, the value A.y would
be computed twice. Is there a smart way to avoid that as to A.y will
be recomputed only if A.x has been changed? Now I can define more
variables to keep track of what is changed but when there are more
variables and the dependency becomes more involved it could be very
complicated. Thanks a lot.

Manu
 
G

George Sakkis

hi all,

If I have a class A with A.x, A.y, A.z. A.y and A.z are property and
in order to compute the value of them, A.y depends on A.x while A.z
depends on A.y and A.x. If I call A.y, and A.z, the value A.y would
be computed twice. Is there a smart way to avoid that as to A.y will
be recomputed only if A.x has been changed? Now I can define more
variables to keep track of what is changed but when there are more
variables and the dependency becomes more involved it could be very
complicated. Thanks a lot.

Manu

I haven't needed to use it myself so far, but PyCells (http://
pycells.pdxcb.net/) seems it might fit the bill.

George
 
B

Bruno Desthuilliers

Manu Hack a écrit :
hi all,

If I have a class A with A.x, A.y, A.z. A.y and A.z are property and
in order to compute the value of them, A.y depends on A.x while A.z
depends on A.y and A.x. If I call A.y, and A.z, the value A.y would
be computed twice. Is there a smart way to avoid that as to A.y will
be recomputed only if A.x has been changed? Now I can define more
variables to keep track of what is changed but when there are more
variables and the dependency becomes more involved it could be very
complicated. Thanks a lot.

A Q&D solution is to use a local cache (usually a dict with propnames as
keys), and have any 'setter' invalidate that cache, ie:

def cached_property(fn):
propname = fn.__name__
def fget(self):
return self._from_cache(propname, fn)
def fset(self, val):
raise AttributeError("%s.%s is readonly" % (self, propname))
return property(fget, fset)

class A(object):
def __init__(self, x):
self._cache = {}
self.x = x

def _from_cache(self, name, fn):
try:
return self._cache[name]
except KeyError:
val = fn(self)
self._cache[name] = val
return val

def _invalidate_cache(self, *names):
for name in names:
try:
del self._cache[name]
except KeyError, e:
#print "%s : %s not in %s" % (e, name, self._cache)
pass

@apply
def x():
def fget(self):
return self._x
def fset(self, x):
self._x = x
# dependencies here - would be nice to have
self._invalidate_cache('y', 'z')
return property(**locals())

@cached_property
def y(self):
return self.x + 2

@cached_property
def z(self):
return self.x * (self.y / 2.0)

The remaining problem is that here, it's x that have the knowledge of
who depends on it. This knowledge would be better expressed as a param
or the @cached_property decorator and stored somewhere, so that the call
to _invalidate cache would take the name of the property itself (here
'x') instead of the list of dependent cached properties. This can be
done at least with a custom metaclass - implementation left as an
exercice to the reader !-)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,177
Messages
2,570,954
Members
47,507
Latest member
codeguru31

Latest Threads

Top