Python类是否支持其他语言的事件?

Hub*_*bro 4 python events class python-2.7

我正在研究我的第一个Python项目,而且我已经错过了我的课程中的事件.也许它甚至不是Python中的事件,但我想在我的类中创建"组",可以添加函数引用.在我的类中的某个时刻,我的组中的所有函数引用都将执行.

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

小智 9

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
Run Code Online (Sandbox Code Playgroud)

  • 但需要注意三点:(1)最好在`__init__`中设置`self.callbacks = {}`并避免伪造的类变量(特别是因为一些可怜的傻瓜可能会覆盖它).(2)同样,`self.callbacks`应该是私有的,即`self._callbacks`.(3)`collections.defaultdict(list)`简化了添加处理程序,因为您不需要区分是否已经有给定事件名称的回调. (3认同)

Mar*_*nen 8

虽然 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)
Run Code Online (Sandbox Code Playgroud)

要使用它,您只需创建一个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
Run Code Online (Sandbox Code Playgroud)

这些事件对象也可以很容易地添加到类或实例中:

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)
Run Code Online (Sandbox Code Playgroud)

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

  • @Timo 那是从早期开始的,我将其修复为使用 `Soldier` 类。谢谢! (2认同)