浅谈Django的Signals的实现

浅谈Django的Signals的实现

在说Signals的实现之前,需要先了解一下发布/订阅模型

模型介绍

发布订阅是一种消息范式,消息的发送者不会将消息直接发送给特定的接收者。而是将消息发送某个频道上,无需了解哪些订阅者可能存在。同样的,订阅者可以表达对一个或多个频道的兴趣,只接收感兴趣的消息,无需了解哪些发布者存在。

发布者

即消息的生产者。

频道

一个消息的通道,标识发布者需要将消息发送到哪一个消息通道。

订阅者

即消息接收者。

Django提供的Signals

与model有关的signals

from django.db.models.signals import class_prepared
from django.db.models.signals import pre_init, post_init
from django.db.models.signals import pre_save, post_save
from django.db.models.signals import pre_delete, post_delete
from django.db.models.signals import m2m_changed
from django.db.models.signals import pre_migrate, post_migrate
from django.db.backends.signals import connection_created

与请求有关的signals

from django.core.signals import request_finished
from django.core.signals import request_started
from django.core.signals import got_request_exception
from django.core.signals import setting_changed

与测试有关的signals

from django.test.signals import setting_changed
from django.test.signals import template_rendered

关于signals的使用就不说明了。

Signals实现

创建一个发布者类

from typing import List, Tuple


class Signal:

    def __init__(self):
        self.receivers: List[Tuple[Tuple[int, int], callable]] = []

    def send(self, sender, **kwargs):
        return [
            (receiver, receiver(signal=self, sender=sender, **kwargs))
            for receiver in self.live_receivers(sender=sender)
        ]

    def connect(self, receiver: callable, sender=None):
        lookup_key: Tuple[int, int] = (id(receiver), id(sender))

        self.receivers.append((lookup_key, receiver))

    def disconnect(self, receiver: callable, sender=None):
        lookup_key: Tuple[int, int] = (id(receiver), id(sender))

        self.receivers.remove((lookup_key, receiver))

    def live_receivers(self, sender) -> List[callable]:
        receivers: List[callable] = []
        sender_key = id(sender)
        for (receiver_id, sender_id), receiver in self.receivers:
            if sender_key == sender_id:
                receivers.append(receiver)
        return receivers


def subscribe(signal, **kwargs):

    def _decorator(func):
        if isinstance(signal, (list, tuple)):
            for s in signal:
                s.connect(func, **kwargs)
        else:
            signal.connect(func, **kwargs)
        return func
    return _decorator

实例化发布者

pre_init = Signal()

实例化一个发布者,pre_init可以在初始化一个实例之前通知订阅者。

创建支持通知的一个类

class Test:

    def __init__(self, *args, **kwargs):
        cls = self.__class__
        pre_init.send(sender=cls, args=args, kwargs=kwargs)
        for key, value in kwargs.items():
            setattr(self, key, value)

订阅pre_intit消息

@subscribe(pre_init, sender=Model)
def test_pre_init(sender, **kwargs):
    print("-------pre_init_______")
    print(sender)
    print(kwargs)
    print("-------end_pre_init_______")

使用

Test(a=1, b=2)

这个时候便会输出test_pre_init中相应的信息

思考

First

Q

如果我有多个类都通知了pre_init,而test_pre_init只想处理某个特定的类发出的消息,应该如何实现?

A

在这个例子中,发送者就是Test类,接收者就是test_pre_init方法,消息通道则是pre_init。当执行send方法时,由pre_init来筛选需要发送给哪些接收者。接收者在注册的时候就已经指定了我要接收谁的消息。

Second

Q

pre_init筛选,时间复杂度太高,为O(n),如何降低时间复杂度。

A

可以换一种存储方式,改成dict来存储接收者。可以把时间复杂度降为O(1),也可以由接收者来控制,当消息来的时候由接收者来选择是否接收该信息。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值