首页 > 编程语言 >【Python】Python 发布订阅模式实现松耦合

【Python】Python 发布订阅模式实现松耦合

时间:2023-09-15 14:04:20浏览次数:42  
标签:订阅 sub Python greet python 耦合 message def

Python 发布订阅模式实现松耦合 发布订阅模式(publish/subscribe或pub/sub)是一种编程模式,消息的发送者(发布者) 不会发送其消息给特定的接收者(订阅者), 而是将发布的消息分为不同的类别直接发布,并 不关注订阅者是谁。 而订阅者可以对一个或多个类别感兴趣.且只接收感兴趣的消息,并且 不关注是哪个发布者发布的消息。 这种发布者和订阅者的解耦可以允许更好的可扩放性和更 为动态的网络拓扑,故受到了大家的喜爱。

发布订阅模式的优点是发布者与订阅者松散的耦合,双方不需要知道对方的存在。 由于主题是被关注的,发布者和订阅者可以对系统拓扑毫无所知。 无论对方是否存在,发送者和 订阅者都可以继续正常操作。 要实现这个模式,就需要有一个中间代理人,在实现中一般被 称为Broker,它维护着发布者和订阅者的关系: 订阅者把感兴趣的主题告诉它,而发布者的 信息也通过它路由到各个订阅者处。简单的实现如下:

from collections import defaultdict

route_table = defaultdict(list)

def sub(self,topic, callback): if callback in route_table[topic]: return route_table[topic].append(callback) def pub(self, topic,*a,**kw): for func in route_tabLe[topic]: func(*a,**kw) 这个实现非常简单,直接放在一个叫Broker.py的模块中(这显然是单件),省去了各种 参数检测、优先处理的需求等,甚至没有取消订阅的函数,但它的确展现了发布订阅模式实 现的最基础的结构,它的应用代码也可以运行。

import Broker

def greeting(name): print (‘Hello, %s.’%name) Broker.sub(‘greet’,greeting) Broker.pub(‘greet’,‘LaiYonghao’) #输出 相对于这个简化版本,blinker和python-message两个模块的实现要完备得多e blinker已 经被用在了多个广受欢迎的项目上,比如flask和django ;而python-message则支持更多丰 富的特性。本节以python-message的使用为例,讲解发布订阅模式的应用场景。

安装python-message相当简单,通过pip安装就可以了。

然后简单验证一下。

import message def hello(name): print (‘hello, %s.’%name) message.sub(‘greet’, hello) message.pub(‘greet’,‘lai’) 运行输出如下:

hello, lai.

接下来用它解决一些实际问题。假定你给项目组开发了一个程序库foo.里面有一个非 常重要的函数——bar。

def bar(): print (‘Haha, Calling bar().’) do_sth() 这个函数如此重要,所以你给它加上了一行输出代码,用以输出日志。后来你的这个程 序库foo被大量使用了,一直运行得很好,直到又一个新项目拖你过去“救火”,因为出了 bug无法査出原因,怀疑是foo的问题。你査看了很久日志,都没有发现他们调用bar()的痕 迹,一问,原来他们是用logging的,标准输出在做Daemon的时候被重定向到了/dev/null。 在临时修改了输出重定向以后,找到了 bug所在,并解决了。然后你开始着手解决这个问 题。一开始你想在你的foo库中引人logging,但原来的项目又不用logging.你在程序库里 引人logging,但谁来初始化它呢?就算你引人了 logging,则你们的项目可能是用logging. getLogger(‘prjA’)获取logger,另一个项目可能是用logging.getLogger(‘prjB’),日后还有新项 目呢!一想到要兼容这么多项目你就头大了。忍痛割爱,把print语句给删除掉吧,又怕曰后 出了问题_己都找不到bug,那还不是自己加班己苦。这个时候,不妨让python-message 来帮你,轻松改一下bar()函数。

import message

LOG_MSG = (‘log’, ‘foo’) def bar(): message.pub(LOG_MSG , ‘Haha, Calling bar().’) do_sth () 在已有的项目中,只需要在项目开始处加上这样的代码,继续把日志放到标准输出。

import message import foo def handle_foo__log_msg(txt): print (txt) message,sub(foo.LOG_MSG, handle_foo_log_msg) 而在那个使用logging的新项目中,则这样修改:

def handler_foo_log_msg(txt): import logging logging.debug(txt) 甚至在一些不关注底层库的日志项目中.直接无视就可以了。通过message,可以轻松 获得库与应用之间的解耦,因为库关注的是要有日志,而不关注日志输出到哪里;应用关注 的是日志要统一放置.但不关注谁往B志文件中输出内容,这正与发布订阅模式的应用场景 不谋而合。

除了简单的sub()/pub()之外,python-message还支持取消订阅(unsub())和中止消息传递。

import message def hello(name): print (‘hello %s’ %name) ctx = message.Context() ctx.discontinued = True return ctx def hi(name): print (‘u cann\rt c me.’) message.sub(‘greet’, hello) message.sub(‘greet’, hi) message.pub(‘greet’, ‘lai’) python-message利用回调函数的返回值来实现取消消息传递, 非常巧妙(读者可以思考 一下为什么能够利用回调确数的返冋值)。 在上面这个例子中,运行后是看不到”u can’t c me.”这一行输出的.因为消息在调用hello()后就中止传递了(Broker使用list对象存储冋调闲数就是为了保证次序)。

python-message是同步调用回调函数的,也就是说谁先sub谁就先被调用。 大部分情况 下这样已经能够满足大分需求,但有时需要后sub的函数先被调用, 这时message.sub函数 通过一个默认参数来支持的f只需要简单地在调用sub的时候加上front=True, 这个回调函 数将被插到所有之前已经sub的回调函数之前:sub(‘greet’,hello,front=True)。

订阅/发布模式是观察者模式的超集,它不关注消息是谁发布的,也不关注消息由谁处 理。 但有时候我们也希望某个自己的类的也能够更方便地订阅/发布消息, 也就是想退化为 观察者模式,python-message同样提供了支持。如以下代码:

from message import observable def greet(people): print (‘hello. %s.’ %people.name) @observable class Foo(object): def init(self, name): print(‘Foo’) self.name = name self.sub(‘greet’, greet) def pub_greet(self): self.pub(‘greet’, self) foo =Foo(‘lai’) foo.pub_greet() python-message提供了类装饰函数observable(),任何class只需要通过它装饰一下就拥有 了 sub/unsub/pub/declare/retract等方法,它们的使用方法跟全局函数是类似的,在此不赞述。

因为python-message的消息i丁阅默认是全局性的,所以有可能产生名字冲突。 在减 少名字冲突方面,可以借鉴java/actionscript3的package起名策略, 比如在应用中定 义消息主题常量FOO=‘com.googlecode.python-message.FOO’, 这祥多个库同时定义 FOO常量也不容易冲突。除此之外,还有_招就是使用uuid,如下:

uuid = ‘bd61825688ci72b345ce07057b2555719’ FOO = uuid + ‘F00’

标签:订阅,sub,Python,greet,python,耦合,message,def
From: https://blog.51cto.com/u_15495755/7480417

相关文章

  • 一个简单的 Python 实现希尔伯特-黄变换(Hilbert-Huang Transform,简称HHT)的例子
     importnumpyasnpfromscipy.signalimportargrelextremadefemd(data):"""经验模式分解(EmpiricalModeDecomposition,EMD)"""#找到极值点max_points,min_points=argrelextrema(data,np.greater,axis=0)max......
  • 「乱搞」用 Python 处理 word 文档
    目录写在前面代码写在最后写在前面大家好啊,我是过气up主Luckyblock,今天给大家来点想看的东西。简单记录如何使用Python中的python-docx模块快速处理word文档。周末就是省赛了,然而板子还没整合起来。因为我是一个懒狗,懒得把代码再慢慢复制进文档里了,于是来学了这个。......
  • 基于python的医疗问诊服务数据采集及可视化分析系统-计算机毕业设计源码+LW文档
    选题的目的、理论与实践意义:选题的目的:随着“互联网+”概念的兴起,有很多传统行业获得了新的发展契机。根据数据统计,用户足不出户就能享受优质的医疗服务,看病贵和看病难这样的问题通过线上医疗问诊得到有效的缓解。系统通过对网站你用户及为平台提供服务的医生,医疗服务数据,评价信息......
  • Python学习笔记-Python异常、模块与包
    了解异常什么是异常当检测到一个错误时,Python解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的“异常”,也就是我们常说的BUG。bug单词的诞生早期计算机采用大量继电器工作,马克二型计算机就是这样的。1945年9月9日,下午三点,马克二型计算机无法正常工作了,技术人员试......
  • Python开发实例(二十)网站检测工具:创建一个测量网站的工具,检查页面加载时间等指标
    在这个实例中,我们将创建一个简单的网站检测工具,使用Python的requests库来测量网站,包括页面加载时间等指标。首先,请确保你已经安装了requests库。如果没有安装,可以通过以下命令来安装:pipinstallrequests下面是一个网站检测工具的Python程序:importrequestsimporttimedefmeasu......
  • python利用openpyxl实现利用excel每行数据填入对应模板批量生成excel
    一、openpyxl常见操作可以参考:https://blog.csdn.net/JunChen681/article/details/1260532061、openpyxl把excel分成了三层Workbook是对工作簿的抽象(工作簿,一个excel文件包含多个sheet。)Worksheet是对表格的抽象(工作表,一个workbook有多个,表名识别,如“sheet......
  • 软件测试|Python Selenium 库安装使用指南
    简介Selenium是一个用于自动化浏览器操作的强大工具,它可以模拟用户在浏览器中的行为,例如点击、填写表单、导航等。在本指南中,我们将详细介绍如何安装和使用Python的Selenium库。安装Selenium库使用以下命令可以通过pip安装Selenium库:pipinstallselenium安装WebDriver......
  • Python接口自动化之Token详解及应用
    问题:在写接口自动化框架时,使用token鉴权后一直不生效,我是把"token:XX"放在请求头header里,调用接口时一直提示登录失效(鉴权失败),然后我就把token发到data中,还是不行,一直困扰着一、Token基本概念及原理1.Token作用为了验证用户登录情况以及减轻服务器的压力,减少频繁的查询数据库,使......
  • python numpy 中的广播操作
    这辈子也学不会numpy。Python中的广播(broadcasting)是一种用于在不同形状的数组之间执行二进制操作的机制,使得它们的形状可以兼容,并且能够按照一定的规则进行元素级操作。这个机制允许你在不显式复制数据的情况下,对形状不同的数组进行操作,这可以节省内存和提高代码的效率。广播......
  • 基于Python+tkinter实现一个简易计算器桌面软件
    你是否想过自己能够开发一个桌面小工具?如果答案是“是”,那么你来对地方了!今天,我们要一起使用Python中的tkinter库,来创建一个简易的桌面计算器。1.为什么选择tkinter?tkinter是Python的标准GUI库。对于初学者来说,它非常友好,因为它提供了大量的预制部件,例如按钮、文本框等,这让开发......