检查一个实例的状态变化

使用情景:

  一个实例在上次“保存”操作之后又被修改了,需要检查它的状态变化以便有选择的保存此实例。

解决方案:

  一个有效的解决方案是创建一个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方法能够敏锐反映出任何细微的改变。

posted @ 2015-09-25 18:15  Kirago  阅读(241)  评论(0编辑  收藏  举报