参考 Blinker Documentation

Blinker 是一个基于Python的强大的信号库,它既支持简单的对象到对象通信,也支持针对多个对象进行组播。Flask的信号机制就是基于它建立的。

Blinker的内核虽然小巧,但是功能却非常强大,它支持以下特性:

  • 支持注册全局命名信号

  • 支持匿名信号

  • 支持自定义命名信号

  • 支持与接收者之间的持久连接与短暂连接

  • 通过弱引用实现与接收者之间的自动断开连接

  • 支持发送任意大小的数据

  • 支持收集信号接收者的返回值

  • 线程安全

创建信号

信号通过signal()方法进行创建:

>>> from blinker import signal
>>> initialized = signal("initialized")
>>> initialized is signal("initialized")
True

每次调用signal('name')都会返回同一个信号对象。因此这里signal()方法使用了单例模式。

订阅信号

使用Signal.connect()方法注册一个函数,每当触发信号的时候,就会调用该函数。该函数以触发信号的对象作为参数,这个函数其实就是信号订阅者。

>>> def subscriber(sender):
... print("Got a signal sent by %r" % sender)
...
>>> ready = signal('ready')
>>> ready.connect(subscriber)
<function subscriber at 0x...>

触发信号

使用Signal.send()方法通知信号订阅者。

下面定义类Processor,在它的go()方法中触发前面声明的ready信号,send()方法以self为参数,也就是说Processor的实例是信号的发送者。

>>> class Processor:
... def __init__(self, name):
... self.name = name
...
... def go(self):
... ready = signal('ready')
... ready.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.

注意到go()方法中的complete信号没?并没有订阅者订阅该信号,但是依然可以触发该信号。如果没有任何订阅者的信号,结果是什么信号也不会发送,而且Blinker内部对这种情况进行了优化,以尽可能的减少内存开销。

订阅特定的发布者

默认情况下,任意发布者触发信号,都会通知订阅者。可以给Signal.connect()传递一个可选的参数,以便限制订阅者只能订阅特定发送者。

>>> 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_b发布的ready信号:

>>> processor_a.go()
Got a signal sent by <Processor a>
Processing.
>>> processor_b.go()
Got a signal sent by <Processor b>
Caught signal from processor_b.
Processing.

通过信号收发数据

可以给send()方法传递额外的关键字参数,这些参数会传递给订阅者。

>>> send_data = signal('send-data')
>>> @send_data.connect
... def receive_data(sender, **kw):
... print("Caught signal from %r, data %r" % (sender, kw))
... return 'received!'
...
>>> result = send_data.send('anonymous', abc=123)
Caught signal from 'anonymous', data {'abc': 123}

send()方法的返回值收集每个订阅者的返回值,拼接成一个元组组成的列表。每个元组的组成为(receiver function, return value)。

匿名信号

前面我们创建的信号都是命名信号,每次调用Signal构造器都会创建一个唯一的信号,,也就是说每次创建的信号是不一样的。下面对前面的Processor类进行改造,将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
...

上面创建的就是匿名信号。on_ready与on_complete是两个不同的信号。

使用修饰器订阅信号

除了使用connect()方法订阅信号之外,使用@connect修饰器可以达到同样的效果。

>>> apc = AltProcessor('c')
>>> @apc.on_complete.connect
... def completed(sender):
... print "AltProcessor %s completed!" % sender.name
...
>>> apc.go()
Alternate processing.
AltProcessor c completed!

尽管这样用起来很方便,但是这种形式不支持订阅指定的发送者。这时,可以使用connect_via()

>>> dice_roll = signal('dice_roll')
>>> @dice_roll.connect_via(1)
... @dice_roll.connect_via(3)
... @dice_roll.connect_via(5)
... def odd_subscriber(sender):
... print("Observed dice roll %r." % sender)
...
>>> result = dice_roll.send(3)
Observed dice roll 3.

优化信号发送

信号通常会进行优化,以便快速的发送。不管有没有订阅者,都可以发送信号。如果发送信号时需要传送的参数要计算很长时间,可以在发送之前使用receivers属性先检查一下是否有订阅者。

>>> bool(signal('ready').receivers)
True
>>> bool(signal('complete').receivers)
False
>>> bool(AltProcessor.on_complete.receivers)
True

还可以检查订阅者是否订阅了某个具体的信号发布者。

>>> signal('ready').has_receivers_for(processor_a)
True

  

blinker库的更多相关文章

  1. Flask备注二(Configurations, Signals)

    Flask备注二(Configuration, Signals) Flask是一个使用python开发Web程序的框架.依赖于Werkzeug提供完整的WSGI支持,以及Jinja2提供templat ...

  2. Inside Flask - signal 信号机制

    Inside Flask - signal 信号机制 singal 在平常的 flask web 开发过程中较少接触到,但对于使用 flask 进行框架级别的开发时,则必须了解相关的工作机制.flas ...

  3. 欢迎来到 Flask 的世界

    欢迎来到 Flask 的世界 欢迎阅读 Flask 的文档.本文档分成几个部分,我推荐您先读 < 安装 >,然后读< 快速上手 >.< 教程 > 比快速上手文档更详 ...

  4. NiftyNet 项目了解

    1. NiftyNet项目概述 NiftyNet项目对tensorflow进行了比较好的封装,实现了一整套的DeepLearning流程.将数据加载.模型加载,网络结构定义等进行了很好的分离,抽象封装 ...

  5. 基于Arduino的音乐动感节奏灯

    1.音乐动感节奏灯是个什么东西? 前段时间听音乐觉得无聊,便想着音乐光听也没意思啊,能不能 “看见” 音乐呢?于是谷歌了一番,发现还真有人做了将音乐可视化的东西,那就是音乐节奏灯.说的简单点就是LED ...

  6. Flask笔记:信号机制

    Flask中有内置的一些信号,也可以通过三方库blinker自定义信号,其实Flask内置的信号也是优先使用的blinker库,如果没有安装blinker才会使用自定义的信号机制.可以通过点击任意导入 ...

  7. python常用库

    本文由 伯乐在线 - 艾凌风 翻译,Namco 校稿.未经许可,禁止转载!英文出处:vinta.欢迎加入翻译组. Awesome Python ,这又是一个 Awesome XXX 系列的资源整理,由 ...

  8. Python 库大全

    作者:Lingfeng Ai链接:http://www.zhihu.com/question/24590883/answer/92420471来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非 ...

  9. pip安装第三方库以及版本

    这篇blog只是写给自己看看的. 今天突然遇到sqlalchemy映射到数据库时,一个字段类型是datetime(6),我这边死活访问不上,之前一直没有问题,最后查明原因,原来是第三方库的版本问题,真 ...

随机推荐

  1. Foundation框架 - 结构体

    一.基础知识 如果要想使用 Foundation 框架的数据类型,那么包含它的主头文件就可以了.即 #import <Foundation/Foundation.h> 补充: Core F ...

  2. Minieye杯第十五届华中科技大学程序设计邀请赛网络赛D Grid(简单构造)

    链接:https://ac.nowcoder.com/acm/contest/560/D来源:牛客网 题目描述 Give you a rectangular gird which is h cells ...

  3. docker(七) 使用dockerfile-maven-plugin插件构建docker镜像

    在dockerfile-maven-plugin插件出现之前,还有一个maven插件是docker-maven-plugin,是由同一个作者创造,作者明确表示推荐使用dockerfile-maven- ...

  4. 使用.net core efcore根据数据库结构自动生成实体类

    源码 github,已更新最新代码 https://github.com/leoparddne/GenEntities/ 使用的DB是mysql,所有先nuget一下mysql.data 创建t4模板 ...

  5. [转帖]Go中的下划线

    Go中的下划线 https://blog.csdn.net/wanglei9876/article/details/50475864 下划线的作用: 在import 时 是仅引入 init 函数 在正 ...

  6. $(function(){})简述

    用jQ的人很多人都是这么开始写脚本的: $(function(){ // do something }); 其实这个就是jq ready()的简写,他等价于: $(document).ready(fu ...

  7. Python——Pycharm创建Django项目

    一.Django的官网 地址:https://www.djangoproject.com/download/ 注:LTS表示一直在维护的版本,建议是带LTS的版本 二.Pycharm下载Django ...

  8. python 元祖字典集合

    一.元祖 1.用途:记录多个值,当多个值没有改变的需求,元祖不能修改,但元祖里的数据的数据可以修改. 2.定义方式:在()内用逗号分隔开多个任意值. 思考:如果定义一个只有一个一个值的元祖. # t ...

  9. socket之黏包

    一.黏包成因 1.tcp协议的拆包机制 当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去. MTU是Maximum Transmission Unit的缩写.意思 ...

  10. 洛谷P1608路径统计

    题目 这个提示一个简单的最短路计数,除了用数组存上最短路的个数的做法以外,还有可以在得出最短路之后,搜索加剪枝的方法来通过该题. 可以反向搜索用A*的方法来通过,但是这个题的去重十分的恶心,需要一些玄 ...