python基于消息的编程
普通的编程方式是引用模块, 调用模块中的函数, 你需要知道模块名和函数名, 还要维护复杂的模块引用关系. 而如果基于消息的编程, 只需要订阅和发布消息即可, 降低了模块间的耦合性.
如下使用示例代码:
from Message import *
def greet(name): print(f"Hello {name}")
sub('greet',greet)
pub('greet','Joe')
具体实现参考了文章: python-message v0.2.x 全接触 https://blog.csdn.net/gzlaiyonghao/article/details/7215315
源码可下载: https://pypi.org/project/message/#files
我简化了实现代码并增加一些注释:
from collections import defaultdict as dd
from copy import copy
__all__ = [
'Broker',
'sub',
'unsub',
'pub',
'declare',
'retract',
'get_declarations',
'has_declaration',
]
class Broker:
'''消息系统对象'''
def __init__(self):
self._router = dd(list) # 存放topic及对应的回调函数, key为topic, value为回调函数的list
self._board = {} # 存放公告牌topic及对应调用参数
def sub(self, topic:str, func:callable, front:bool = False):
'''订阅指定的消息, 当消息触发时调用对应的回调函数. 其中front表示是否插入到最前面'''
if func in self._router[topic]: return # 不支持重复订阅
# 确定是否插入最前面
if front: self._router[topic].insert(0, func)
else: self._router[topic].append(func)
# 下面是公告牌的功能, 如果公告牌有此主题, 则立即触发回调函数
if topic in self._board:
a, kw = self._board[topic]
func(*a, **kw)
def unsub(self, topic:str, func:callable):
'''取消订阅指定的消息'''
if func not in self._router[topic]: return # 不支持取消未订阅的消息
try: self._router[topic].remove(func)
except ValueError: pass # 移除不存在的函数也不报错
def pub(self, topic:str, *a, **kw):
'''发布消息, 调用所有订阅了此消息的回调函数'''
# 使用copy的意思是在多线程的情况下也能够正常工作
for func in copy(self._router[topic]): func(*a, **kw)
def declare(self, topic:str, *a, **kw):
'''发布公告牌, 存放回调函数所需要的参数'''
self._board[topic] = (a, kw)
self.pub(topic, *a, **kw) # 立即发布消息
def retract(self, topic:str):
'''取消公告牌'''
try:self._board.pop(topic)
except KeyError:pass
def get_declarations(self):
'''获取所有发布的公告'''
return self._board.keys()
def has_declaration(self, topic:str):
'''判断是否发布过公告'''
return topic in self._board
_broker = Broker()
sub = _broker.sub
unsub = _broker.unsub
pub = _broker.pub
declare = _broker.declare
retract = _broker.retract
get_declarations = _broker.get_declarations
has_declaration = _broker.has_declaration
补充模块的__init__.py
文件代码:
from .message import __all__
#__all__ = __all__
from .message import *
#print(__all__)