Django的信号和观察者模式
今天想到给以前写的东西增加缓存支持,每次数据库发生变化之后主动的去修改缓存。当然最笨的方法就是在每次更新数据库的代码后面写一段更新缓存的代码,我们能不能在数据库被更新之后对外发一个信号呢,更新缓存的函数收到这个信号就知道数据库发生了变化。
这个在django中其实已经有了实现,就是siginal,用法大致的这样的
1def my_callback(sender, **kwargs):
2 print("Request finished!")
3
4from django.core.signals import request_finished
5
6request_finished.connect(my_callback)
这个其实就是经典的观察者模式的实现。
http://dongweiming.github.io/python-observer.html 有一段代码,我认为很好,直接贴过来了(其实后来发现原版在 https://github.com/faif/python-patterns/blob/master/observer.py )
1# 这个是观察者基类
2class Subject(object):
3 def __init__(self):
4 self._observers = []
5
6 # 添加依赖的对象
7 def attach(self, observer):
8 if not observer in self._observers:
9 self._observers.append(observer)
10
11 # 取消添加
12 def detach(self, observer):
13 try:
14 self._observers.remove(observer)
15 except ValueError:
16 pass
17
18 # 这里只是通知上面注册的依赖对象新的变化
19 def notify(self, modifier=None):
20 for observer in self._observers:
21 # 可以设置过滤条件,对不符合过滤条件的更新
22 if modifier != observer:
23 observer.update(self)
24
25
26# 观察者类
27class Data(Subject):
28 def __init__(self, name=''):
29 super(Data, self).__init__()
30 self.name = name
31 self._data = 0
32
33 # python2.6新增的写法,获取属性为property,设置属性为(假设属性名字为x)@x.setter,删除为@x.deleter
34 @property
35 def data(self):
36 return self._data
37
38 @data.setter
39 def data(self, value):
40 self._data = value
41 self.notify()
42
43# 这里有2个被观察者,也就是依赖的对象,每次Data有改变,这2个view都会变动
44class HexViewer(object):
45 def update(self, subject):
46 print 'HexViewer: Subject %s has data 0x%x' % (subject.name, subject.data)
47
48class DecimalViewer(object):
49 def update(self, subject):
50 print 'DecimalViewer: Subject %s has data %d' % (subject.name, subject.data)
51
52
53if __name__ == '__main__':
54
55 data1 = Data('Data 1')
56 data2 = Data('Data 2')
57 view1 = DecimalViewer()
58 view2 = HexViewer()
59 data1.attach(view1)
60 data1.attach(view2)
61 data2.attach(view2)
62 data2.attach(view1)
63
64 print "Setting Data 1 = 10"
65 data1.data = 10
66 print "Setting Data 2 = 15"
67 data2.data = 15
68 print "Setting Data 1 = 3"
69 data1.data = 3
70 print "Setting Data 2 = 5"
71 data2.data = 5
72 print "Update data1's view2 Because view1 is be filtered"
73 data1.notify(modifier=view1)
74 print "Detach HexViewer from data1 and data2."
75 data1.detach(view2)
76 data2.detach(view2)
77 print "Setting Data 1 = 10"
78 data1.data = 10
79 print "Setting Data 2 = 15"
80 data2.data = 15
输出结果是
1Setting Data 1 = 10
2DecimalViewer: Subject Data 1 has data 10
3HexViewer: Subject Data 1 has data 0xa
4Setting Data 2 = 15
5HexViewer: Subject Data 2 has data 0xf
6DecimalViewer: Subject Data 2 has data 15
7Setting Data 1 = 3
8DecimalViewer: Subject Data 1 has data 3
9HexViewer: Subject Data 1 has data 0x3
10Setting Data 2 = 5
11HexViewer: Subject Data 2 has data 0x5
12DecimalViewer: Subject Data 2 has data 5
13Update data1's view2 Because view1 is be filtered
14HexViewer: Subject Data 1 has data 0x3
15Detach HexViewer from data1 and data2.
16Setting Data 1 = 10
17DecimalViewer: Subject Data 1 has data 10
18Setting Data 2 = 15
19DecimalViewer: Subject Data 2 has data 15
20[Finished in 0.1s]
如果要像django那样直接传递一个类而不是类实例,我们可以使用classmethod或者staticmethod,仿照http://www.the5fire.com/python-observer.html 我自己写了一个
1#event.py
2class Event(object):
3 _observers = []
4
5 def __init__(self, subject):
6 self.subject = subject
7
8 @classmethod
9 def register(cls, observer):
10 if observer not in cls._observers:
11 cls._observers.append(observer)
12
13 @classmethod
14 def unregister(cls, observer):
15 if observer in cls._observers:
16 cls._observers.remove(observer)
17
18 @classmethod
19 def notify(cls, subject):
20 event = cls(subject)
21 for observer in cls._observers:
22 observer(event)
23
24#process.py
25from event import Event
26
27
28class Data(object):
29 def __init__(self, value):
30 self.value = value
31 Event.notify("set value")
32
33
34#main.py
35from event import Event
36from process import Data
37
38
39def log(event):
40 print "log", event.subject
41
42
43def log1(event):
44 print "log1", event.subject
45
46
47Event.register(log)
48Event.register(log1)
49Data(123)
50
51Event.unregister(log1)
52Data(456)
输出是
1log set value
2log1 set value
3log set value
是不是更像django里面的用法了呢~