Django信号的实现原理本质是设计模式中的观察者模式,浅谈Python设计模式 -- 观察者模式,也可以叫做发布-订阅模式,信号对象维护一个订阅者列表,当信号被触发时,它会遍历订阅者,依次通知它们。
先来回顾一下信号的定义和使用:
from django.dispatch import receiver, Signal
# 信号定义
node_approved = Signal()
# 信号的注册
@receiver(node_approved)
def on_node_approved(sender, instance, **kwargs):
print(‘接收到信号’)
# 信号的触发
node_approved.send(sender=xx, instance=yy)
源码分析:
1、先来看receiver这个装饰器:
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::
@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)):
for s in signal:
s.connect(func, **kwargs)
else:
signal.connect(func, **kwargs)
return func
return _decorator
逻辑很简单的一个装饰器,核心是调用Signal信号对象的connect方法,也就是上面举例中的node_approved这个对象的connect方法。
2、接着看Signal 的connect方法:
class Signal:
def __init__(self, providing_args=None, use_caching=False):
"""
Create a new signal.
"""
self.receivers = []
...
def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
from django.conf import settings
# If DEBUG is on, check that we got a good 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))
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__'):
ref = weakref.WeakMethod
receiver_object = receiver.__self__
receiver = ref(receiver)
weakref.finalize(receiver_object, self._remove_receiver)
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()
只看倒数第二行:self.receivers.append((lookup_key, receiver)),如果你了解观察者模式就很好理解这行代码的意图:将被装饰的信号处理函数(on_node_approved)注册到self.recervers属性中,也就是观察者模式中讲的主题维护观察者列表行为。
一旦有了这个观察者列表,那么就很容易做到信号被触发时,通知每个观察者的目的。下面看看源码是怎么实现的:
3、信号触发send源码:
class Signal:
def __init__(self, providing_args=None, use_caching=False):
"""
Create a new signal.
"""
self.receivers = []
...
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
...
for (receiverkey, r_senderkey), receiver in self.receivers:
if r_senderkey == NONE_ID or r_senderkey == senderkey:
receivers.append(receiver)
...
non_weak_receivers = []
for receiver in receivers:
if isinstance(receiver, weakref.ReferenceType):
# Dereference the weak reference.
receiver = receiver()
if receiver is not None:
non_weak_receivers.append(receiver)
else:
non_weak_receivers.append(receiver)
return non_weak_receivers
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)
]
send理解起来也不难:遍历self._live_receivers(),依次调用各个receiver。而_live_receivers通过源码也可以看出本质还是遍历前面提到的观察者列表self.recervers属性。