檢查一個實例的狀態變化

使用情景: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方法可以敏銳反映出任何細微的改變。繼承

相關文章
相關標籤/搜索