消息队列(1) -- Blinker信号库 for Python(单应用)

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章