我承认,是标题党!哈哈哈哈哈哈哈,本来是准备写一篇“Django Signal源码解析——从Observer Pattern到Django Signal”的,但是我又不能一两句话把观察者模式讲清楚的,干脆就直接贴了一些相关的链接……后面越写越多、越拖越长就干脆改成简析算了……
本文只作为个人学习记录,仅供参考,如有误欢迎讨论指正
题图来自 unsplash,原图链接🔗
观察者模式
“观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。[1]”
“观察者模式是一种行为设计模式, 允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。[2]”
观察者模式有多种实现方式,比较经典的一种实现,有四个角色:Subject(被观察对象抽象类)、Observer(观察者抽象类)、ConcreateObserver(具体观察者)、ConcreateSubject(具体的被观察者)……
这里就不赘述了……可以参见下面的链接
Signal简述
可以先看看Django的官方文档
与观察者模式对应:sender就是被观察者、receiver是观察者。有所不同的就是多了个signal,也就是“信号”(也可使说是信号的“管理者”)。在普通观察者模式中,被观察对象抽象类中一般会维护一个观察者抽象类的引用列表,以此来传递消息,而django中则是借助Signal类进一步解耦,通过Signal类进行“信号传递”,具体解析如下:
1. signal
信号类,维护一个receivers列表,用于存储sender和receiver的对应记录
signal.connect()
用于往signal.receivers
中添加对应的receiver和sender记录
signal_send()
,在signal.receivers
中查找sender对应的receivers并将对应信号(参数)传递给对应的receivers
django预置了一些信号对象,pre_init、post_init、pre_save、post_save等,这些都是直接或间接的继承自Siganl类,单一职责原则,每个具体的信号对象只负责存储“一类信号”,比如前面列举的四个信号对象都是django model相关的。以pre_save为例,只有在调用某个model对象的save方法时(前)才会调用pre_save.send()发送相关数据(“信号”)。也就是说pre_save这个Signal对象的receivers列表中存储的sender都是model而receiver都是我们希望在进行model.save()前调用的函数来进行的一些相关操作。
2. sender
信号的发送方,即调用signal.send()
方法的类。比如,所有model类的父类都是BaseModel,而BaseModel有一个__init__函数在该函数中,首先就会调用pre_init.send(self.__class__,**kwargs)
发送即将开始init的信号,然后再进行具体的init操作,在init之后再调用post_init.send
发送init完成的信号。故这里的BaseModel类就是这两个信号对象(pre_init、post_init)的sender。而我们的model类都是继承自BaseModel,init方法也是继承来的,故此时的sender就是我们定义的model类。
3. receiver
信号的接收方,为可调用对象,一般是个函数,接收signal.send()传递过来的参数,再进行相关的操作
假如我们有个model对象为People,我们想要在构造每一个People时(new_people = People(name="haha",email="haha@mail.com")
)打印出其相关信息,我们可以用一个函数打印其信息:
1
2
|
def print_info(*args,**kwargs):
print(f"args: {args}\nkwargs: {kwargs}")
|
然后使用pre_init.connect(print_info, sender=People)
将其与sender绑定起来,即在pre_init的receivers列表中存一条对应的记录:
1
|
((People_id,print_info_id),print_info_weakref)
|
此处的print_info
函数即为一个receiver,pre_init信号中People类对应的receiver。
当然也可以直接使用装饰器,不过也是同样的道理
1
2
3
|
@receiver(pre_init,sender=People)
def print_info_before_init(sender,*args,**kwargs):
print(f"args: {args}\nkwargs: {kwargs}")
|
最终打印出来的结果是
1
2
|
args: []
kwargs: {'name':'haha','email':'haha.mail.com'}
|
原理简析
django的整个信号模块都是依赖Signal这个类完成的
1. Signal的属性和__init__方法
1.receivers:一个列表用于存取该Signal中对应的sender和receiver并存下receiver的引用(或弱引用),在调用connect()方法时会往receivers中添加对应关系,而在调用send()方法时会在其中查找对应关系
数据结构:
1
2
3
4
5
6
7
8
|
# 如果在调用connect方法添加时weak参数为True(默认为True)
# 则存储receiver的弱引用,在垃圾回收时会自动处理
[((receiver_id,sender_id),receiver_weakref),] # 弱引用
# 如果connect方法的weak参数为False
# 则存储receiver的强引用,最终需要调用disconnect销毁记录
# 否则相关变量无法被垃圾回收,也就是”内存泄露“
[((receiver_id,sender_id),receiver),] # 强引用
|
2.providing_args:该signal接受的参数的列表
3.lock:threading.lock,对相应操作加锁以保证安全
4.use_caching:bool类型,标识是否使用缓存,如果使用缓存我们会缓存每一个sender类的receivers并存储到self.sender_receivers_cache中
5.sender_receivers_cache:signal对每一个sender的receivers进行缓存
数据结构:
1
2
3
4
|
{
"sender1": [receiver1_weakref, receiver2_weakref,...],
"sender2": [receiver3_weakref, receiver4_weakref,...],
}
|
每次send时会使用sender_receivers_cache快速判断该sender是否有receiver,如果有则再调用_live_receivers获取其对应的receivers的强应用列表
6._dead_receivers:bool类型,用于标识self.reivers中是否含有失效的 receiver的弱引用。当弱引用指向的receiver被垃圾回收时会调用Signal._remove_receiver方法将_dead_receivers置为True
2. Signal中的方法
说在前面:receiver和sender都必须是可哈希的
1.connect:将receiver和sender通过对应的Signal连接起来(即将对应记录添加到对应Signal对象的receivers属性列表中)
参数解析:
- receiver:需要接收信号的receiver
- sender:发送该信号的sender
- weak:是否使用弱引用,默认为True。如果为True则存储receiver对应的weakref否则直接存储强引用。由于设置了
weakref.finalize(receiver_object, self._remove_receiver)
若存储弱引用则可以在receiver垃圾回收时自动清除Signal.receivers中对应的记录(见下面的分析)若存储强引用则需要先手动调用disconnect方法删除Signal.receivers中对应的记录否则该receiver将无法被垃圾回收导致“内存泄漏”
- dispatch_uid:默认为None,如果使用了该值(一般为一个字符串),相当于为该receiver自定义个唯一名称(不允许重复),存储时将会使用
dispatch_uid
代替id(sender)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
if dispatch_uid:
lookup_key = (dispatch_uid, _make_id(sender)) # 如果设置了dispatch_uid则使用其期待id值作为唯一标识
else:
lookup_key = (_make_id(receiver), _make_id(sender))
if weak: # 如果使用弱引用
ref = weakref.ref
receiver_object = receiver
# Check for bound methods
if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'):
# 如果该receiver是对象方法,则receiver_object为包含该receiver的对象
ref = weakref.WeakMethod
receiver_object = receiver.__self__
receiver = ref(receiver) # 获取该receiver的弱引用
weakref.finalize(receiver_object, self._remove_receiver)
# receiver_object垃圾回收时调用self._remove_receiver将self._dead_receivers置为True
# 在后面的send或send_robust中对self.receivers加锁后清理
with self.lock:
self._clear_dead_receivers()
if not any(r_key == lookup_key for r_key, _ in self.receivers): # 检查是否有重复
self.receivers.append((lookup_key, receiver))
self.sender_receivers_cache.clear()
# 每次connect或disconnect后都要清除缓存
# 该缓存会在send时构建
|
2.disconnect:从对应Signal对象的receivers列表中删除对应记录(一般用于手动删除在connect时使用weak=False的情况,即该条记录存的是receiver的强引用,若不手动删除该条记录会导致“内存泄漏”,与gc的引用计数有关)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
def disconnect(self, receiver=None, sender=None, dispatch_uid=None):
"""
将signal中的receiver与sender“断开连接”
该函数主要针对那些在connect时weak参数为False的情况,即signal的receivers列表
中存储的是rceiver的强引用
如果在connect时使用的弱引用(weak=True), 则不需要手动调用disconnect方法
receiver和sender的记录会被自动删除
"""
if dispatch_uid:
lookup_key = (dispatch_uid, _make_id(sender)) # 如果提供了dispatch_uid则使用其替代 id(receiver)
else:
lookup_key = (_make_id(receiver), _make_id(sender))
disconnected = False
with self.lock:
self._clear_dead_receivers() # 先清除dead_receivers
for index in range(len(self.receivers)):
(r_key, _) = self.receivers[index]
if r_key == lookup_key: # 根据lookup_key在self.receivers中查找
disconnected = True
del self.receivers[index] # 删除self.receivers中的对应记录
break
self.sender_receivers_cache.clear() # 变更后清除缓存
return disconnected
|
3.has_listeners:检查某个sender是否有receiver
1
2
|
def has_listeners(self, sender=None):
return bool(self._live_receivers(sender))
|
4.send:给某个sender对应的所有receivers发送相应的信号,首先会检查这个sender是否有receivers。如果有,先调用_live_receivers获取当前sender对应的所有的receiver的强引用,然后依次调用每个receiver传入相关的参数。最后,返回[(receiver,response),]
(这里的receiver是强引用即可直接调用的receiver函数,response是调用receiver函数返回的结果)
参数解析:
- sender:发送信号的类
- **named:传递给该sender对应的receivers的参数
1
2
3
4
5
6
7
8
|
def send(self, sender, **named):
if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
return []
return [
(receiver, receiver(signal=self, sender=sender, **named))
for receiver in self._live_receivers(sender) # _live_receivers返回的是该sender对应的所有的receivers的强引用
]
|
注: 当调用某个receiver发生异常时程序会直接退出,可能导致其他剩下的receiver并没有被调用执行
5.send_robust:“健壮版”的send,当调用某个receiver发生异常时会将异常捕捉并存储起来,故该sender的所有的receiver都能被调用执行。该函数会返回[(receiver,response / err),]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
def send_robust(self, sender, **named):
# 相比send方法调用receiver方法异常时直接退出,send_robust会收集异常并返回
if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
return []
# Call each receiver with whatever arguments it can accept.
# Return a list of tuple pairs [(receiver, response), ... ].
responses = []
for receiver in self._live_receivers(sender):
try:
response = receiver(signal=self, sender=sender, **named)
except Exception as err:
responses.append((receiver, err))
# 如果调用receiver出现异常则捕获并返回相应的异常而不影响其他receiver的调用
else:
responses.append((receiver, response))
return responses
|
6._clean_dead_receivers:清除Signal.receivers中的dead receiver,该函数会在send或send_robust中对Signal.receivers加锁了的情况下调用
1
2
3
4
5
6
7
8
9
10
|
def _clear_dead_receivers(self):
# Note: caller is assumed to hold self.lock.
# 清除dead_receivers
if self._dead_receivers:
self._dead_receivers = False # 还原_dead_receivers为False
self.receivers = [
r for r in self.receivers
if not(isinstance(r[1], weakref.ReferenceType) and r[1]() is None)
# 弱引用,并且该弱引用对应的对象已消亡
]
|
注: 其实这里说的清除dead receiver,就是删除Signal.receivers中那些原函数/对象(即receiver函数或包含receievr函数的对象)已经被垃圾回收了的weakref,至于那些在connect时使用weak=False添加的强引用则需要手动调用disconnect进行手动清除,否则可能会导致内存泄露
7._live_receivers:先通过self._dead_receivers检查是否有dead receivers如果有则先过滤一遍Signal.receivers,然后从Signal.receivers中找出该sender对应所有的receiver,并返回包含所有receiver强引用的列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
def _live_receivers(self, sender):
"""
Filter sequence of receivers to get resolved, live receivers.
This checks for weak references and resolves them, then returning only
live receivers.
"""
receivers = None
if self.use_caching and not self._dead_receivers: # 如果使用缓存并且self.receivers中无dead_receivers
receivers = self.sender_receivers_cache.get(sender) # 直接从缓存中获取
# We could end up here with NO_RECEIVERS even if we do check this case in
# .send() prior to calling _live_receivers() due to concurrent .send() call.
# 其实我们在调用_live_receivers方法的send方法中已经检查过一次了
if receivers is NO_RECEIVERS:
return []
if receivers is None: # 如果不使用缓存或者存在待清理的dead receivers
with self.lock:
self._clear_dead_receivers() # 先清理dead_receivers
senderkey = _make_id(sender)
receivers = []
for (receiverkey, r_senderkey), receiver in self.receivers: # 遍历self.receivers进行查找匹配
if r_senderkey == NONE_ID or r_senderkey == senderkey: # 如果senderkey为id(None)或匹配
receivers.append(receiver) # 将receiver(weakref)添加到receivers中
# 更新缓存
if self.use_caching: # 如果使用缓存
if not receivers: # 如果该sender没有receiver (receivers为空)
self.sender_receivers_cache[sender] = NO_RECEIVERS # 将cache中该sender的receivers置为空
else: # 如果该sender存在对应的receiver
# Note, we must cache the weakref versions.
# 注意我们应该缓存弱引用
self.sender_receivers_cache[sender] = receivers # 将该sender及其所有的receivers缓存起来
non_weak_receivers = []
for receiver in receivers: # receivers要么为[] 要么为[receiver]
if isinstance(receiver, weakref.ReferenceType):
# Dereference the weak reference.
# https://www.kite.com/python/docs/weakref.ReferenceType
receiver = receiver() # 获取弱引用所指的实际对象,若已消亡则返回None,否则返回对象的强引用(“可直接调用”)
if receiver is not None: # weakref所指的原对象尚未消亡
non_weak_receivers.append(receiver) # 添加实际可调用的receiver对象
else:
non_weak_receivers.append(receiver) # 不是弱引用则直接添加
return non_weak_receivers # 可调用对象receiver的列表
|
8._remove_receivers:将Signal._dead_receivers置为True,在后面调用send或send_robust方法时会先检查_dead_receivers,如果为True则先调用_clean_dead_receivers进行清理
1
2
3
4
5
6
7
8
9
10
11
12
|
def _remove_receiver(self, receiver=None):
# Mark that the self.receivers list has dead weakrefs. If so, we will
# clean those up in connect, disconnect and _live_receivers while
# holding self.lock. Note that doing the cleanup here isn't a good
# idea, _remove_receiver() will be called as side effect of garbage
# collection, and so the call can happen while we are already holding
# self.lock.
# 标记self.receivers中有dead weakrefs(weakref所指对象已消亡).如果存在dead receiver
# 程序会在调用connect, disconnect和_live_receivers时在加锁的情况下清除dead weakrefs
# 注意在这里执行具体的清除操作并不是一个好主意,在垃圾回收时_remove_receiver()会被
# 自动触发,而我们可能在其他操作中对receivers加了锁,此时清理receivers将会导致冲突
self._dead_receivers = True
|
注: 由于在connect方法中的设置了weakref.finalize(receiver_object, self._remove_receiver)
,在receiver_object垃圾回收时会自动触发_remove_receiver方法。如果将清除过滤Signal.receivers的过程放在这个函数中,而其它地方可能正在在加锁的情况下使用receivers则会导致冲突。故_remove_receivers只将Signal._dead_receivers置为True,从而把清理dead receiver的过程延迟到了connect、disconnect、send、send_robust中加锁进行
3. 大概流程
通过Signal.connect向Signal.receivers中注册对应的((receiver_id, sender_id), receiver_weakref)
记录,然后sender会在恰当的时候调用恰当的Signal并使用Signal.send发送恰当的信号给对应的所有receiver。这就是信号的大致传递过程
4. 很奇怪的一点
程序里面有个奇怪的None
,在connect时不指定sender则sender,默认sender就会是None,而在sender中调用Siganl.send方法该方法又会调用Signal._live_receivers方法,在Signal._live_receivers中会寻找该sender对应的receiver,而匹配条件居然是if r_senderkey == NONE_ID or r_senderkey == senderkey
,sender为None的receivers也会被匹配?有点不太理解这里的做法
5. 多说一句
异步问题,当一个sender注册的receiver过多时,调用send方法时可能会非常占用时间造成阻塞,观察者模式好像都有这个问题,可以使用异步改善?(在网上看到的,具体如何改善我还没搞清楚,等弄明白了再补上……)
源码简析
源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
|
import threading
import weakref
from django.utils.inspect import func_accepts_kwargs
# 返回对象的id,在connect存储记录和send查询记录时会用到
# 注意:sender和receiver都必须是可哈希的
def _make_id(target):
if hasattr(target, '__func__'): # 如果是对象方法
return (id(target.__self__), id(target.__func__))
return id(target)
# django.utils.inspect.func_accepts_kwargs
# 检查传入的函数是否接受**kwargs参数
def func_accepts_kwargs(func):
return any(
p for p in inspect.signature(func).parameters.values()
if p.kind == p.VAR_KEYWORD
)
NONE_ID = _make_id(None)
# A marker for caching
NO_RECEIVERS = object()
class Signal:
"""
所有signal的基类
Base class for all signals
Internal attributes:
receivers
{ receiverkey (id) : weakref(receiver) }
"""
def __init__(self, providing_args=None, use_caching=False):
"""
创建新的signal
providing_args: 在调用send()方法时可以传入这个signal的参数列表
"""
self.receivers = []
# ((receiver_id,sender_id),receiver_weakref)或者是((dispatch_id,sender_id),receiverweak_ref)
# 具体是强引用还是weakref取决于connect时的weak参数是True还是False
# 默认weak为True使用弱引用,若为False则删除该条记录时需要手动disconnect
if providing_args is None:
providing_args = []
self.providing_args = set(providing_args)
self.lock = threading.Lock() # 加锁保证操作数据时线程安全
self.use_caching = use_caching # 是否使用缓存
# 为了便利即使不使用缓存我们也会创建空的缓存
# 关于缓存需要注意的一点是:如果use_caching为True,对于每一个sender
# 我们会在sender_receiver_cache中缓存其所有的receiver。
# 该缓存在调用.connect()或者.disconnect()时会被清除,而在调用send时会被填充
self.sender_receivers_cache = weakref.WeakKeyDictionary() if use_caching else {}
self._dead_receivers = False
# receivers属性列表中是否含有“失效”的receiver引用(已被垃圾回收)
# 在_clear_dead_receivers方法中会检查其值,如果为True则代表其中
# 有失效的receiver引用,则过滤清理一遍receivers属性列表
def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
"""
# 通过signal将receiver连接到sender
Arguments:
receiver
A function or an instance method which is to receive signals.
Receivers must be hashable objects.
If weak is True, then receiver must be weak referenceable.
Receivers must be able to accept keyword arguments.
If a receiver is connected with a dispatch_uid argument, it
will not be added if another receiver was already connected
with that dispatch_uid.
sender
The sender to which the receiver should respond. Must either be
a Python object, or None to receive events from any sender.
weak
Whether to use weak references to the receiver. By default, the
module will attempt to use weak references to the receiver
objects. If this parameter is false, then strong references will
be used.
dispatch_uid
An identifier used to uniquely identify a particular instance of
a receiver. This will usually be a string, though it may be
anything hashable.
"""
from django.conf import settings
# If DEBUG is on, check that we got a good receiver
# 如果在DEBUG模式下,则检查该receiver是否满足要求
if settings.configured and settings.DEBUG:
assert callable(receiver), "Signal receivers must be callable."
# Check for **kwargs
if not func_accepts_kwargs(receiver):
raise ValueError("Signal receivers must accept keyword arguments (**kwargs).")
if dispatch_uid:
lookup_key = (dispatch_uid, _make_id(sender)) # 如果设置了dispatch_uid作为某receiver的标识
else:
lookup_key = (_make_id(receiver), _make_id(sender)) # 没有dispatch_uid则使用receiver的id
if weak: # 如果使用weak则存储该receiver的弱引用,否则就直接存储强引用(强引用最终需要手动disconnect)
ref = weakref.ref
receiver_object = receiver # 强引用
# Check for bound methods
if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'): # 检查是否是对象方法
# https://docs.python.org/zh-cn/3/library/weakref.html#weakref.WeakMethod
ref = weakref.WeakMethod
receiver_object = receiver.__self__ # 包含该对象方法的对象
# 使用弱引用替代强引用
receiver = ref(receiver) # 普通函数方法则直接取其弱引用
# 当receiver方法、或包含该方法对象消亡时从signal的receivers列表中清除该条记录
weakref.finalize(receiver_object, self._remove_receiver)
# https://docs.python.org/zh-cn/3/library/weakref.html#weakref.finalize
# 当receiver对象垃圾回收时调用_remove_receiver置self._dead_receivers = True
with self.lock:
self._clear_dead_receivers() # 清除、过滤dead_receivers
if not any(r_key == lookup_key for r_key, _ in self.receivers): # 查重
self.receivers.append((lookup_key, receiver)) # 添加((receiver_id,sender_id),receiver_weakref)
self.sender_receivers_cache.clear() # 变更后清除缓存
def disconnect(self, receiver=None, sender=None, dispatch_uid=None):
"""
将signal中的receiver与sender“断开连接”
该函数主要针对那些在connect时weak参数为False的情况,即signal的receivers列表
中存储的是rceiver的强引用
如果在connect时使用的弱引用(weak=True), 则不需要手动调用disconnect方法
receiver和sender的记录会被自动删除
Arguments:
receiver
The registered receiver to disconnect. May be none if
dispatch_uid is specified.
sender
The registered sender to disconnect
dispatch_uid
the unique identifier of the receiver to disconnect
"""
if dispatch_uid:
lookup_key = (dispatch_uid, _make_id(sender)) # 如果提供了dispatch_uid则使用其替代 id(receiver)
else:
lookup_key = (_make_id(receiver), _make_id(sender))
disconnected = False
with self.lock:
self._clear_dead_receivers() # 先清除dead_receivers
for index in range(len(self.receivers)):
(r_key, _) = self.receivers[index]
if r_key == lookup_key: # 根据lookup_key在self.receivers中查找
disconnected = True
del self.receivers[index] # 删除self.receivers中的对应记录
break
self.sender_receivers_cache.clear() # 变更后清除缓存
return disconnected
def has_listeners(self, sender=None):
# 通过调用_live_receivers方法看sender的recivers是否为空
return bool(self._live_receivers(sender))
def send(self, sender, **named):
"""
Send signal from sender to all connected receivers.
从sender中发送信号给所有连接到该sender的receiver
If any receiver raises an error, the error propagates back through send,
terminating the dispatch loop. So it's possible that all receivers
won't be called if an error is raised.
Arguments:
sender
The sender of the signal. Either a specific object or None.
named
Named arguments which will be passed to receivers.
Return a list of tuple pairs [(receiver, response), ... ].
"""
if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
# 如果该signal得receivers为空或者该sender对应的receivers缓存为空
return []
return [
(receiver, receiver(signal=self, sender=sender, **named)) # 这里的receiver就是具体的receiver函数
for receiver in self._live_receivers(sender)
# _live_receiver返回的是receiver的可调用对象(就是具体的receiver函数,强引用)的列表
]
# 返回[(receiver函数,调用receiver函数的结果)]如果调用某个receiver时发生异常程序会直接退出
# 导致其他的剩下的receiver并未被执行
def send_robust(self, sender, **named):
"""
Send signal from sender to all connected receivers catching errors.
Arguments:
sender
The sender of the signal. Can be any Python object (normally one
registered with a connect if you actually want something to
occur).
named
Named arguments which will be passed to receivers. These
arguments must be a subset of the argument names defined in
providing_args.
Return a list of tuple pairs [(receiver, response), ... ].
If any receiver raises an error (specifically any subclass of
Exception), return the error instance as the result for that receiver.
相比send方法调用receiver方法异常时直接退出,send_robust会收集异常并返回
"""
if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
return []
# Call each receiver with whatever arguments it can accept.
# Return a list of tuple pairs [(receiver, response), ... ].
responses = []
for receiver in self._live_receivers(sender):
try:
response = receiver(signal=self, sender=sender, **named)
except Exception as err:
responses.append((receiver, err))
# 如果调用receiver出现异常则捕获并返回相应的异常而不影响其他receiver的调用
else:
responses.append((receiver, response))
return responses
def _clear_dead_receivers(self):
# Note: caller is assumed to hold self.lock.
# 清除dead_receivers
if self._dead_receivers:
self._dead_receivers = False
self.receivers = [
r for r in self.receivers
if not(isinstance(r[1], weakref.ReferenceType) and r[1]() is None)
# 弱引用,并且该弱引用对应的对象已消亡
]
def _live_receivers(self, sender):
"""
Filter sequence of receivers to get resolved, live receivers.
This checks for weak references and resolves them, then returning only
live receivers.
"""
receivers = None
if self.use_caching and not self._dead_receivers: # 如果使用缓存并且self.receivers中无dead_receivers
receivers = self.sender_receivers_cache.get(sender) # 直接从缓存中获取
# We could end up here with NO_RECEIVERS even if we do check this case in
# .send() prior to calling _live_receivers() due to concurrent .send() call.
# 在调用_live_receivers方法的方法中已经检查过一次了
if receivers is NO_RECEIVERS:
return []
if receivers is None: # 如果不使用缓存或者存在待清理的dead receivers
with self.lock:
self._clear_dead_receivers() # 先清理dead_receivers
senderkey = _make_id(sender)
receivers = []
for (receiverkey, r_senderkey), receiver in self.receivers: # 遍历self.receivers进行查找匹配
if r_senderkey == NONE_ID or r_senderkey == senderkey: # 如果senderkey为id(None)或匹配
receivers.append(receiver) # 将receiver(weakref)添加到receivers中
# 更新缓存
if self.use_caching: # 如果使用缓存
if not receivers: # 如果该sender没有receiver (receivers为空)
self.sender_receivers_cache[sender] = NO_RECEIVERS # 将cache中该sender的receivers置为空
else: # 如果该sender存在对应的receiver
# Note, we must cache the weakref versions.
# 注意我们应该缓存弱引用
self.sender_receivers_cache[sender] = receivers # 将该sender及其所有的receivers缓存起来
non_weak_receivers = []
for receiver in receivers: # receivers要么为[] 要么为[receiver]
if isinstance(receiver, weakref.ReferenceType):
# Dereference the weak reference.
# https://www.kite.com/python/docs/weakref.ReferenceType
receiver = receiver() # 获取弱引用所指的实际对象,若已消亡则返回None,否则返回对象的强引用(“可直接调用”)
if receiver is not None: # ref所指的原对象尚未消亡
non_weak_receivers.append(receiver) # 添加实际可调用的对象
else:
non_weak_receivers.append(receiver) # 不是弱引用则直接添加
return non_weak_receivers # 可调用对象receiver的列表
def _remove_receiver(self, receiver=None):
# Mark that the self.receivers list has dead weakrefs. If so, we will
# clean those up in connect, disconnect and _live_receivers while
# holding self.lock. Note that doing the cleanup here isn't a good
# idea, _remove_receiver() will be called as side effect of garbage
# collection, and so the call can happen while we are already holding
# self.lock.
# 标记self.receivers中有dead weakrefs(weakref所指对象已消亡).如果存在dead receiver
# 程序会在调用connect, disconnect和_live_receivers时在加锁的情况下清除dead weakrefs
# 注意在这里执行具体的清除操作并不是一个好主意,在垃圾回收时_remove_receiver()会被
# 自动触发,而我们可能在其他操作中对receivers加了锁,此时清理receivers将会导致冲突
self._dead_receivers = True
def receiver(signal, **kwargs):
"""
A decorator for connecting receivers to signals. Used by passing in the
signal (or list of signals) and keyword arguments to connect::
用来将receivers连接到对应signal的装饰器. 使用时传入signal(或者是装有signal的列表)
以及相关的关键字参数
@receiver(post_save, sender=MyModel)
def signal_receiver(sender, **kwargs):
...
@receiver([post_save, post_delete], sender=MyModel)
def signals_receiver(sender, **kwargs):
...
"""
def _decorator(func):
if isinstance(signal, (list, tuple)): # 传入的是signal列表
for s in signal:
s.connect(func, **kwargs) # 调用connect方法,sender的信息包含在kwargs中了
else:
signal.connect(func, **kwargs)
return func
return _decorator
|
除此之外,django已经为我们预置了一些信号:
pre_init
、post_init
、pre_save
、post_save
、pre_delete
、post_delete
、m2m_change
……(详细用法见官方文档)
django在django/db/models/signals.py
中定义了与数据库Model相关的信号对象:
在django/core/signals.py
中定义了与request和setting相关的信号对象:
django为我们预置这些信号对象,并且在适当的时候调用适当的信号对象发送适当的“信号”。比如在django/db/models/base.py
中,在Model类的__init__方法开始位置调用pre_init.send()在__init__的结束位置调用post_init.send(),而我们的model又都是继承自该Model。
django为我们预置好了一些信号,并且在做相应操作时会发送对应的信号,所以我们需要做的就是在需要用到时,定义对应的receiver去接收对应的信号📶即可。当然我们也可以直接实例化或继承Signal类来定义我们自己的信号。具体的使用可以参见这篇博客
一些可能有用的链接