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