I'm not sure this is possible, but I would like to have
a list of objects
A=[a,b,c,d,...,z]
where, in the midst of a lot of processing I might do something like,
A[0].do_something_which_changes_the_properties()
which alter the properties of the object 'a'.
The trick is that I would like A to be mysteriously aware that
something about the object 'a' has changed so that when I revisit A,
I will know that the other items in the list need to be refreshed to
reflect the changes in A as a result of changing 'a'.
Even better would be to automatically percolate the subsequent changes
that resulted from altering 'a' for the rest of the items in the list.
Naturally, all of these items are related in some parent-child
fashion.
that might be a lot to ask, however.
Any advice appreciated.
I think Python Cookbook has a recipe which deals with this.
- 6.12 Checking an Instance for Any State Change.
Were you able to get this? If not, let me know. I will try to type it
in here - (it is a big recipe, so not doing it now)
Actually, I have the python cookbook, but cannot find the recipe you
mention. maybe I have an older version?
Mine is 2nd Edition.
for posterity's sake, here's the recipe in question:
import copy
class ChangeCheckerMixin(object):
containerItems = {dict: dict.iteritems, list: enumerate}
immutable = False
def snapshot(self):
''' create a "snapshot" of self's state -- like a shallow
copy, but
recursing over container types (not over general
instances:
instances must keep track of their own changes if
needed). '''
if self.immutable:
return
self._snapshot = self._copy_container(self.__dict__)
def makeImmutable(self):
''' the instance state can't change any more, set .immutable
'''
self.immutable = True
try:
del self._snapshot
except AttributeError:
pass
def _copy_container(self, container):
''' semi-shallow copy, recursing on container types only '''
new_container = copy.copy(container)
for k, v in self.containerItems[type(new_container)]
(new_container):
if type(v) in self.containerItems:
new_container[k] = self._copy_container(v)
elif hasattr(v, 'snapshot'):
v.snapshot( )
return new_container
def isChanged(self):
''' True if self's state is changed since the last snapshot
'''
if self.immutable:
return False
# remove snapshot from self.__dict__, put it back at the end
snap = self.__dict__.pop('_snapshot', None)
if snap is None:
return True
try:
return self._checkContainer(self.__dict__, snap)
finally:
self._snapshot = snap
def _checkContainer(self, container, snapshot):
''' return True if the container and its snapshot differ '''
if len(container) != len(snapshot):
return True
for k, v in self.containerItems[type(container)](container):
try:
ov = snapshot[k]
except LookupError:
return True
if self._checkItem(v, ov):
return True
return False
def _checkItem(self, newitem, olditem):
''' compare newitem and olditem. If they are containers, call
self._checkContainer recursively. If they're an instance
with
an 'isChanged' method, delegate to that method.
Otherwise,
return True if the items differ. '''
if type(newitem) != type(olditem):
return True
if type(newitem) in self.containerItems:
return self._checkContainer(newitem, olditem)
if newitem is olditem:
method_isChanged = getattr(newitem, 'isChanged', None)
if method_isChanged is None:
return False
return method_isChanged( )
return newitem != olditem
if __name__ == '__main__':
class eg(ChangeCheckerMixin):
def __init__(self, *a, **k):
self.L = list(*a, **k)
def __str__(self):
return 'eg(%s)' % str(self.L)
def __getattr__(self, a):
return getattr(self.L, a)
x = eg('ciao')
print 'x =', x, 'is changed =', x.isChanged( )
# emits: x = eg(['c', 'i', 'a', 'o']) is changed = True
# now, assume x gets saved, then...:
x.snapshot( )
print 'x =', x, 'is changed =', x.isChanged( )
# emits: x = eg(['c', 'i', 'a', 'o']) is changed = False
# now we change x...:
x.append('x')
print 'x =', x, 'is changed =', x.isChanged( )
# emits: x = eg(['c', 'i', 'a', 'o', 'x']) is changed = True