blinker是一个python的信号库,既支持简单的对象到对象通信,也支持针对多个对象进行组播,信号就是在框架的核心功能或者一些Flask拓展发生动作时所发送的通知,可以帮助解耦应用,解耦就是模块间的耦合严重了,修改一些东西,可能会牵扯到很多其他的地方,所以需要减少类之间的相互依赖,所以需要代码解耦。
先来一个例子:
创建信号并发送消息
- 参数:信号名,信号的发送者
- 作用:根据信号名创建信号,然后发送信号,参数是发送者
def signal_send(name, source='anoymous', **kw):
name_signal = signal(name) # 这里根据name创建一个信号
ret = name_signal.send(source, **kw) # 发送一个信号
return ret
接受信号
- 参数:信号名,订阅者(用于收到信号就调用函数)
- 作用:得到信号对象,绑定订阅者
def signal_listen(name, handler):
name_signal = signal(name) # 这里其实没有创建新的信号,就是之前的name信号
s.connect(handler, weak=False) # 绑定了订阅者
信号接受装饰器:
- 参数:信号名
- 针对的func:装饰器原理是针对func的,传入的func就是这个信号的订阅者
def signal_handle(name, *args, **kw):
s_name = name
def wrapper(func):
def inner(*args, **kw):
func(*args, **kw)
signal_listen(s_name, func)
return inner
return wrapper
使用上面的套件:
# 这里监听了信号test,一旦收到消息就调用里面的函数
@signal_handle('test')
def recv(sender, **kw):
print("Got a signal sent by %r, data %r" % (sender, kw))
return 'receive ok'
signal_send('test', source='123', test=1234)
# 这里相当于创建了信号,并且发送了消息123
# 输出是Got a signal sent by '123', data {'test': 1234}
创建信号:
>>> from blinker import signal
>>> initialized = signal("initialized")
>>> initialized is signal("initialized")
True
可以看到信号是通过singal()方法创建的,而且当传入的参数一致时,调用该方法都是返回同一个信号对象。
订阅信号,即信号触发就会调用函数
>>> def subscriber(sender): # 这个函数就是**订阅者!!!!!**
... print("Got a signal sent by %r" % sender)
...
>>> ready = signal('ready') # 创建信号
>>> ready.connect(subscriber) # 注册一个函数,只要触发信号就调用该函数
信号触发的函数会以触发信号的对象作为参数!
信号的触发:
使用Signal.send()方法来通知信号订阅者,即调用信号注册的函数
>>> class Processor:
... def __init__(self, name):
... self.name = name
...
... def go(self):
... ready = signal('ready') # 这里的信号对象还是上面创建的信号对象
ready.connect(xxx) # 这里绑定一个函数
... ready.send(self)
# send()的参数是self,所以这个类的实例就是信号的发送者
... print("Processing.")
... complete = signal('complete')
# 没有注册信号订阅者的信号也可以触发该信号,但是什么信号都不会发送
... complete.send(self)
...
... def __repr__(self):
... return '<Processor %s>' % self.name
...
>>> processor_a = Processor('a') # 创建了一个对象
>>> processor_a.go() # 这里相当于发送了一个信号,信号的发送者是自己本身这个对象
Got a signal sent by <Processor a>
Processing.
订阅特定的发布者,即只有特定的发送者才能触发这个订阅函数
默认情况下,所有的发送者都能触发信号,通知订阅者,但是可以在connect()中传递一个可选参数sender=?
>>> def b_subscriber(sender):
... print("Caught signal from processor_b.")
... assert sender.name == 'b'
...
>>> processor_b = Processor('b')
>>> ready.connect(b_subscriber, sender=processor_b) # 这里指定特定的发送者
<function b_subscriber at 0x...>
这样的话,Processor实例化出来的其他对象都不会通知这个信号订阅者
通过信号收发数据:
>>> send_data = signal('send-data') # 这里创建信号对象
>>> @send_data.connect # 绑定信号订阅者,相当于signal.connect(subscriber)
... def receive_data(sender, **kw):
... print("Caught signal from %r, data %r" % (sender, kw))
... return 'received!'
...
>>> result = send_data.send('anonymous', abc=123) # send()方法可以加上额外的关键字参数
Caught signal from 'anonymous', data {'abc': 123}
匿名信号
前面创建的信号都是命名信号,每次调用都会创建一个唯一的信号,还可以将signal作为类属性
>>> from blinker import Signal
>>> class AltProcessor:
... on_ready = Signal() # 这里创建了两个匿名信号
... on_complete = Signal()
...
... def __init__(self, name):
... self.name = name
...
... def go(self):
... self.on_ready.send(self)
... print("Alternate processing.")
... self.on_complete.send(self)
...
... def __repr__(self):
... return '<AltProcessor %s>' % self.name
使用装饰器绑定订阅函数
可以使用:
@signal.connect
的方式来指定订阅者,但是这样就不能指定特定的发送者。
就可以使用:
@signal.connect_via(x)
x就是指定了sender
优化信号的发送,检查该信号是否有订阅者
>>> bool(signal('ready').receivers)
True
>>> signal('ready').has_receivers_for(processor_a)
True