哪些Python库提供独立的事件系统?

254 投票
16 回答
315540 浏览
提问于 2025-04-15 12:44

我知道有一个叫做 pydispatcher 的东西,但Python里肯定还有其他和事件相关的库。

有哪些库可以用呢?

我对那些大框架里的事件管理器不感兴趣,我更想要一个简单的、基础的解决方案,这样我可以轻松地进行扩展。

16 个回答

77

我们使用了一个叫做EventHook的东西,这是Michael Foord在他的事件模式中提到的。

只需要在你的类里添加EventHooks,就可以了:

class MyBroadcaster()
    def __init__():
        self.onChange = EventHook()

theBroadcaster = MyBroadcaster()

# add a listener to the event
theBroadcaster.onChange += myFunction

# remove listener from the event
theBroadcaster.onChange -= myFunction

# fire event
theBroadcaster.onChange.fire()

我们给Michael的类增加了一个功能,可以从一个对象中移除所有的监听器,最后得到了这个:

class EventHook(object):

    def __init__(self):
        self.__handlers = []

    def __iadd__(self, handler):
        self.__handlers.append(handler)
        return self

    def __isub__(self, handler):
        self.__handlers.remove(handler)
        return self

    def fire(self, *args, **keywargs):
        for handler in self.__handlers:
            handler(*args, **keywargs)

    def clearObjectHandlers(self, inObject):
        for theHandler in self.__handlers:
            if theHandler.im_self == inObject:
                self -= theHandler
118

我一直是这样做的:

class Event(list):
    """Event subscription.

    A list of callable objects. Calling an instance of this will cause a
    call to each item in the list in ascending order by index.

    Example Usage:
    >>> def f(x):
    ...     print 'f(%s)' % x
    >>> def g(x):
    ...     print 'g(%s)' % x
    >>> e = Event()
    >>> e()
    >>> e.append(f)
    >>> e(123)
    f(123)
    >>> e.remove(f)
    >>> e()
    >>> e += (f, g)
    >>> e(10)
    f(10)
    g(10)
    >>> del e[0]
    >>> e(2)
    g(2)

    """
    def __call__(self, *args, **kwargs):
        for f in self:
            f(*args, **kwargs)

    def __repr__(self):
        return "Event(%s)" % list.__repr__(self)

不过,就像我看到的其他所有东西一样,这个没有自动生成的pydoc,也没有签名,这真让人失望。

299

PyPI 包

截至2022年10月,这里是PyPI上与事件相关的包,按最近的发布日期排序。

还有更多

有很多库可以选择,它们使用的术语各不相同(事件、信号、处理器、方法分发、钩子等)。

我在努力保持对上述包的概述,以及这里回答中提到的技术。

首先,来了解一些术语……

观察者模式

最基本的事件系统风格是“处理方法的集合”,这是对观察者模式的简单实现。

基本上,处理方法(可调用对象)被存储在一个数组中,当事件“触发”时,每个方法都会被调用。

发布-订阅

观察者事件系统的缺点是你只能在实际的事件对象(或处理器列表)上注册处理器。所以在注册时,事件必须已经存在。

这就是第二种事件系统风格存在的原因:发布-订阅模式。在这里,处理器不是在事件对象(或处理器列表)上注册,而是在一个中央调度器上。通知者只与调度器沟通。要监听什么,或者发布什么是由“信号”决定的,信号不过是一个名字(字符串)。

中介者模式

可能也会有兴趣的是:中介者模式

钩子

钩子系统通常用于应用程序插件的上下文中。应用程序包含固定的集成点(钩子),每个插件可以连接到这个钩子并执行某些操作。

其他“事件”

注意:threading.Event并不是上述意义上的“事件系统”。它是一个线程同步系统,其中一个线程等待另一个线程“信号”事件对象。

网络消息库也常常使用“事件”这个术语;有时这些概念相似,有时则不然。它们当然可以跨越线程、进程和计算机的边界。比如pyzmqpymqTwistedTornadogeventeventlet

弱引用

在Python中,持有对方法或对象的引用可以确保它不会被垃圾回收器删除。这可能是有利的,但也可能导致内存泄漏:链接的处理器永远不会被清理。

一些事件系统使用弱引用而不是常规引用来解决这个问题。

关于各种库的一些话

观察者风格的事件系统:

  • zope.event展示了这个如何工作的基本框架(见Lennart的回答)。注意:这个例子甚至不支持处理器参数。
  • LongPoke的“可调用列表”实现展示了如何通过子类化list来非常简约地实现这样的事件系统。
  • Felk的变体EventHook也确保了被调用者和调用者的签名。
  • spassig的EventHook(Michael Foord的事件模式)是一个简单明了的实现。
  • Josip的Valued Lessons事件类基本上是相同的,但使用set而不是list来存储集合,并实现了__call__,这都是合理的补充。
  • PyNotify在概念上类似,也提供了变量和条件的附加概念(“变量改变事件”)。主页不可用。
  • axel基本上是一个处理器集合,具有更多与线程、错误处理等相关的功能。
  • python-dispatch要求事件源类从pydispatch.Dispatcher派生。
  • buslane是基于类的,支持单个或多个处理器,并提供广泛的类型提示。
  • Pithikos的Observer/Event是一个轻量级设计。

发布-订阅库:

  • blinker有一些很酷的功能,比如自动断开连接和基于发送者的过滤。
  • PyPubSub是一个稳定的包,承诺提供“促进调试和维护主题和消息的高级功能”。
  • pymitter是Node.js EventEmitter2的Python移植版,提供命名空间、通配符和TTL。
  • PyDispatcher似乎强调在多对多发布等方面的灵活性。支持弱引用。
  • louie是重写的PyDispatcher,应该可以在“各种上下文中工作”。
  • pypydispatcher基于(你猜对了……)PyDispatcher,也可以在PyPy中工作。
  • django.dispatch是重写的PyDispatcher,“接口更有限,但性能更高”。
  • pyeventdispatcher基于PHP的Symfony框架的事件调度器。
  • dispatcher是从django.dispatch提取的,但相当老旧。
  • Cristian Garcia的EventManger是一个非常简短的实现。

其他:

  • pluggy包含一个钩子系统,被pytest插件使用。
  • RxPy3实现了可观察模式,允许合并事件、重试等。
  • Qt的信号和槽可以通过PyQtPySide2获得。当在同一线程中使用时,它们作为回调工作,或者在两个不同线程之间作为事件(使用事件循环)。信号和槽的限制在于它们只能在从QObject派生的类的对象中工作。

撰写回答