【问题标题】:Does Python classes support events like other languages?Python 类是否像其他语言一样支持事件?
【发布时间】:2011-05-27 23:27:22
【问题描述】:

我正在处理我的第一个 Python 项目,但我的课程中已经缺少事件。也许它在 Python 中甚至不被称为事件,但我想在我的类中创建“组”,可以添加函数引用。在我班级的某个时刻,我组中的所有函数引用都会执行。

这是内置在 Python 中的吗? (我现在用的是2.7)

【问题讨论】:

  • 请解释一下您所说的事件是什么意思。有例外(各种名称和目的),但它们并不是真正的事件。如果您指的是诸如“onClick”或“onKeyDown”之类的东西,您可以从内置的 Tkinter 库或其他与 Python 编写和集成的 GUI 库中获得它(据我所知,您无法集成它)使用非 GUI 程序)。您究竟在寻求什么行为?
  • 也许你应该搜索“python观察者模式”。有很多关于这个主题的帖子。
  • 当然可以实现。当然,stdlib 无法满足您的特定需求,因为它可能是高度特定于域的。它可能像self.events[eventtype].add(handler)for handler in self.events[eventtype]: handler(...) 一样简单,或者根据您的需要需要更多的簿记。提供详细信息。
  • Chrono Kitsune:我正在寻求一种非常简单的行为。我有处理各种任务(托管服务器、处理数据库等)的类。我希望我的 Python 脚本能够告诉这些类对象在某个事件发生时运行给定的函数,例如 clientConnectedprocessingComplete

标签: python events class python-2.7


【解决方案1】:

Python 没有内置任何类型的事件系统,但它可以很简单地实现。例如:

class ObjectWithEvents(object):
    callbacks = None

    def on(self, event_name, callback):
        if self.callbacks is None:
            self.callbacks = {}

        if event_name not in self.callbacks:
            self.callbacks[event_name] = [callback]
        else:
            self.callbacks[event_name].append(callback)

    def trigger(self, event_name):
        if self.callbacks is not None and event_name in self.callbacks:
            for callback in self.callbacks[event_name]:
                callback(self)

class MyClass(ObjectWithEvents):
    def __init__(self, contents):
        self.contents = contents

    def __str__(self):
        return "MyClass containing " + repr(self.contents)

def echo(value): # because "print" isn't a function...
    print value

o = MyClass("hello world")
o.on("example_event", echo)
o.on("example_event", echo)
o.trigger("example_event") # prints "MyClass containing \"Hello World\"" twice

【讨论】:

  • 三个注意事项:(1) 最好在__init__ 中设置self.callbacks = {} 并避免伪造类变量(尤其是因为一些可怜的傻瓜可能会覆盖它)。 (2) 同样,self.callbacks 应该是私有的,即self._callbacks。 (3) collections.defaultdict(list) 简化了添加处理程序,因为您不需要区分给定事件名称是否已经存在回调。
  • 总的来说,我同意。我选择使用这种方法是因为它是最独立的,为了方便不太熟悉该语言的人(如果我使用过__init__,那么用户将不得不处理super()的快乐)。
  • 你的资料吓到我了!你是如何用 70 多个金徽章获得“1”的!?
  • @not2qubit Jeremy 的帐户似乎已被溢出版主暂停。作为副作用,这意味着帐户的声誉分数被锁定为“1”。详情在这里:stackoverflow.blog/2009/04/06/a-day-in-the-penalty-box/…
  • @NeilsSchoenfelder 哇。我不知道这甚至存在。谢谢澄清链接。
【解决方案2】:

虽然 Jeremy Banks 的回答效果很好,但这并不是大多数人所说的“pythonic”。由于这个问题很容易通过搜索引擎出现,所以这里有一个替代答案,它尝试使用我的经验中的最佳约定:

class Event:
    def __init__(self):
        self.listeners = []

    def __iadd__(self, listener):
        """Shortcut for using += to add a listener."""
        self.listeners.append(listener)
        return self

    def notify(self, *args, **kwargs):
        for listener in self.listeners:
            listener(*args, **kwargs)

要使用它,您只需创建一个Event 对象,然后通过直接操作listeners 列表或使用+= 快​​捷方式来注册侦听器回调。然后,您使用notify() 方法调用所有侦听器。传递给notify() 方法的任何参数和关键字参数都将转发给侦听器。

这是一个完整的例子:

>>> my_event = Event()
>>> def print_person_info(name, age, sex):
...     print("Hello! I am {}, I'm a {}-year-old {}".format(name, age, sex))
...
>>> my_event += print_person_info
>>> my_event.notify('Markus', 23, 'male')
Hello! I am Markus, I'm a 23-year-old male

这些事件对象也可以轻松添加到类或实例中:

class Soldier:
    # An event on a class level.
    # Listening to just this will notify you of *any* person dying. 
    e_death = Event()

    def __init__(self, name, health):
        self.name = name
        self.health = health

        # Instance level event.
        # Using this you need to listen to each person separately.
        self.e_eat = Event()

    def eat(self, amount):
        self.health += amount
        self.e_eat.notify(self, amount=amount)

    def hurt(self, damage):
        self.health -= damage
        if self.health <= 0:
            Soldier.e_death.notify(self)

当然,像这样混合类和实例级别的事件通常是个坏主意,我只是出于演示目的才这样做。如果不确定,请使用实例级事件。

【讨论】:

  • Person 定义在哪里?
  • @Timo 那是从早期的零开始的,我将其修复为使用 Soldier 类。谢谢!
  • @MarkusMeskanen 您能否更新您的“完整示例”以在实际运行该代码之前也使用需要执行的导入(或其他操作)?此外,我不太明白 那些 与最后一个 Soldier 示例的联系。也许您可以让您的示例独立,而不是与 Py CLI 混合?
【解决方案3】:

如果有人对 python 版本 3+ 的事件支持感兴趣,可以使用事件通知库 (https://pypi.org/project/event-notifier/)

此外,还有一个很好的替代品观察列表here:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-08
    • 2016-06-13
    • 2016-04-02
    • 2015-03-27
    • 1970-01-01
    相关资源
    最近更新 更多