使用情景:
一个实例在上次“保存”操作之后又被修改了,需要检查它的状态变化以便有选择的保存此实例。
解决方案:
一个有效的解决方案是创建一个mixin类,这个类可以从多个类继承并能对一个实例的状态进行快照操作,这样就可以用此实例的当前状态和上次的快照做比较了,来判断是否被修改过了。
1 import copy 2 class ChangeCheckerMixin: 3 containerItems = {dict: dict.iteritems, list: enumerate} 4 immutable = False 5 def snapshot(self): 6 '''创建self状态的快照',就像浅拷贝,但只对容器的类型进行递归。。'' 7 if self.immutable: 8 return 9 self._snapshot = self._copy_container(self.__dict__) 10 def makeImmutable(self): 11 '''实例状态无法被修改 设置.immutable''' 12 self.immutable = True 13 try: 14 del sekf._snapshot 15 except AttributeError: 16 pass 17 def _copy_container(self, container): 18 '''对容器类型拷贝''' 19 new_container = copy.copy(container) 20 for k, v in self.containerItems[type(new_container)](new_container): 21 if type(v) in self.containerItems: 22 new_container[k] = self._copy_container(v) 23 elif hasattr(v, 'snapshot'): 24 v.snapshot() 25 return new_container 26 def isChanged(self): 27 if self.immutable: 28 return False 29 snap = self.__dict__.pop('_snapshot', None) 30 if snap is None: 31 return True 32 try: 33 return self._checkContainer(self.__dict__, snap) 34 finally: 35 self._snapshot = snap 36 def _checkContainer(self, container, snapshot): 37 if len(container) != len(snapshot): 38 return True 39 for k, v in self.containerItems[type(container)](container): 40 try: 41 ov = snapshot[k] 42 except LookUpError: 43 return True 44 if self._checkItem(v, ov): 45 return True 46 return False 47 def _checkItem(self, newitem, olditem): 48 '''比较新旧元素,如果它们是容器类型,递归调用self._checkContainer''' 49 if type(newitem) != type(olditem): 50 return True 51 if type(newitem) in self.containerItems: 52 return self._checkContainer(newitem, olditem) 53 if newitem is olditem: 54 method_isChanged = getattr(newitem, 'isChanged', None) 55 if method_isChanged is None: 56 return False 57 return method_isChanged 58 return newitem != olditem 59 60 61 if __name__ == '__main__': 62 class eg(ChangeCheckerMixin): 63 def __init__(self, *arg, **kwargs): 64 self.L = list(*arg, **kwargs) 65 def __str__(self): 66 return 'eg(%s)' % str(self.L) 67 def __getattr__(self, a): 68 return getattr(self.L, a) 69 X = eg('ciao') 70 print 'x =',X,'is changed =', X.isChanged() 71 X.snapshot() 72 print 'x =',X,'is changed =', X.isChanged() 73 X.append('x') 74 print 'x =',X,'is changed =', X.isChanged()
最后输入结果如下图:
在eg类中只是从ChangeCheckerMixin派生,因为不需要其他基类。即使从list派生也没什么用处,因为状态检查功能只能被用于那些保存在实例的字典里的状态。所以必须将一个list对象放在实例的字典中,并根据情况将某些任务委托给它,(上述示例中通过__getattr__委托了所有的非特殊方法),这样就可以看到isChanged方法能够敏锐反映出任何细微的改变。