当前位置: 首页 > news >正文

Django信号机制源码分析(观察者模式)

Django信号的实现原理本质是设计模式中的观察者模式,浅谈Python设计模式 -- 观察者模式,也可以叫做发布-订阅模式,信号对象维护一个订阅者列表,当信号被触发时,它会遍历订阅者,依次通知它们。

先来回顾一下信号的定义和使用:

from django.dispatch import receiver, Signal# 信号定义
node_approved = Signal()# 信号的注册
@receiver(node_approved)
def on_node_approved(sender, instance, **kwargs):print(‘接收到信号’)# 信号的触发
node_approved.send(sender=xx, instance=yy)

源码分析:

1、先来看receiver这个装饰器:

def receiver(signal, **kwargs):"""A decorator for connecting receivers to signals. Used by passing in thesignal (or list of signals) and keyword arguments to connect::@receiver(post_save, sender=MyModel)def signal_receiver(sender, **kwargs):...@receiver([post_save, post_delete], sender=MyModel)def signals_receiver(sender, **kwargs):..."""def _decorator(func):if isinstance(signal, (list, tuple)):for s in signal:s.connect(func, **kwargs)else:signal.connect(func, **kwargs)return funcreturn _decorator

逻辑很简单的一个装饰器,核心是调用Signal信号对象的connect方法,也就是上面举例中的node_approved这个对象的connect方法。

2、接着看Signal 的connect方法:

class Signal:def __init__(self, providing_args=None, use_caching=False):"""Create a new signal."""self.receivers = []...def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):from django.conf import settings# If DEBUG is on, check that we got a good receiverif settings.configured and settings.DEBUG:assert callable(receiver), "Signal receivers must be callable."# Check for **kwargsif not func_accepts_kwargs(receiver):raise ValueError("Signal receivers must accept keyword arguments (**kwargs).")if dispatch_uid:lookup_key = (dispatch_uid, _make_id(sender))else:lookup_key = (_make_id(receiver), _make_id(sender))if weak:ref = weakref.refreceiver_object = receiver# Check for bound methodsif hasattr(receiver, '__self__') and hasattr(receiver, '__func__'):ref = weakref.WeakMethodreceiver_object = receiver.__self__receiver = ref(receiver)weakref.finalize(receiver_object, self._remove_receiver)with self.lock:self._clear_dead_receivers()if not any(r_key == lookup_key for r_key, _ in self.receivers):self.receivers.append((lookup_key, receiver))self.sender_receivers_cache.clear()

只看倒数第二行:self.receivers.append((lookup_key, receiver)),如果你了解观察者模式就很好理解这行代码的意图:将被装饰的信号处理函数(on_node_approved)注册到self.recervers属性中,也就是观察者模式中讲的主题维护观察者列表行为。

一旦有了这个观察者列表,那么就很容易做到信号被触发时,通知每个观察者的目的。下面看看源码是怎么实现的:

3、信号触发send源码:

class Signal:def __init__(self, providing_args=None, use_caching=False):"""Create a new signal."""self.receivers = []...def _live_receivers(self, sender):"""Filter sequence of receivers to get resolved, live receivers.This checks for weak references and resolves them, then returning onlylive receivers."""...receivers = None...for (receiverkey, r_senderkey), receiver in self.receivers:if r_senderkey == NONE_ID or r_senderkey == senderkey:receivers.append(receiver)...non_weak_receivers = []for receiver in receivers:if isinstance(receiver, weakref.ReferenceType):# Dereference the weak reference.receiver = receiver()if receiver is not None:non_weak_receivers.append(receiver)else:non_weak_receivers.append(receiver)return non_weak_receiversdef send(self, sender, **named):if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:return []return [(receiver, receiver(signal=self, sender=sender, **named))for receiver in self._live_receivers(sender)]

send理解起来也不难:遍历self._live_receivers(),依次调用各个receiver。而_live_receivers通过源码也可以看出本质还是遍历前面提到的观察者列表self.recervers属性。

相关文章:

  • docker学习(二十一、network使用示例container、自定义)
  • 【自然语言处理】【大模型】 ΨPO:一个理解人类偏好学习的统一理论框架
  • Flink1.17实战教程(第二篇:DataStream API)
  • 云原生机器学习平台cube-studio开源项目及代码简要介绍
  • Python 网络编程之搭建简易服务器和客户端
  • 智慧监控平台/AI智能视频EasyCVR接口调用编辑通道详细步骤
  • andriod安卓水果商城系统课设
  • 程序员如何高效学习技术?
  • 算法设计与分析 | 矩阵连乘
  • 清除conda和pip缓存的方法
  • STM32 基础知识(探索者开发板)--103讲 通用定时器
  • MACBOOK 通过iterm2连接堡垒机跳转服务器
  • 【用unity实现100个游戏之19】制作一个3D传送门游戏,实现类似鬼打墙,迷宫,镜子,任意门效果
  • json转换(json与对象互转、json与list互转、JSONObject与Map互转)
  • SketchUp各版本安装指南
  • SegmentFault for Android 3.0 发布
  • 2017 年终总结 —— 在路上
  • Javascripit类型转换比较那点事儿,双等号(==)
  • Java读取Properties文件的六种方法
  • learning koa2.x
  • Mysql5.6主从复制
  • SpriteKit 技巧之添加背景图片
  • 动态魔术使用DBMS_SQL
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 力扣(LeetCode)357
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 山寨一个 Promise
  • 使用 QuickBI 搭建酷炫可视化分析
  • 小程序 setData 学问多
  • 源码之下无秘密 ── 做最好的 Netty 源码分析教程
  • 你对linux中grep命令知道多少?
  • ​ArcGIS Pro 如何批量删除字段
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • $.ajax()参数及用法
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (C语言)二分查找 超详细
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (学习日记)2024.02.29:UCOSIII第二节
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (一)kafka实战——kafka源码编译启动
  • (已解决)什么是vue导航守卫
  • (转)Sql Server 保留几位小数的两种做法
  • (转)大型网站架构演变和知识体系
  • .net 微服务 服务保护 自动重试 Polly
  • .Net(C#)自定义WinForm控件之小结篇
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args
  • .Net8 Blazor 尝鲜
  • .NET企业级应用架构设计系列之结尾篇
  • .NET是什么
  • .pop ----remove 删除
  • @Autowired多个相同类型bean装配问题
  • @staticmethod和@classmethod的作用与区别
  • [1181]linux两台服务器之间传输文件和文件夹