使用情景:app
一個實例在上次「保存」操做以後又被修改了,須要檢查它的狀態變化以便有選擇的保存此實例。spa
解決方案:code
一個有效的解決方案是建立一個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()
最後輸入結果以下圖:blog
在eg類中只是從ChangeCheckerMixin派生,由於不須要其餘基類。即便從list派生也沒什麼用處,由於狀態檢查功能只能被用於那些保存在實例的字典裏的狀態。因此必須將一個list對象放在實例的字典中,並根據狀況將某些任務委託給它,(上述示例中經過__getattr__委託了全部的非特殊方法),這樣就能夠看到isChanged方法可以敏銳反映出任何細微的改變。繼承