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

12 投票
3 回答
17752 浏览
提问于 2025-04-16 18:29

我正在做我的第一个Python项目,但我发现我的类里缺少一些事件。也许在Python里不叫事件,但我想在我的类里创建一些“组”,可以把函数的引用放进去。到某个时刻,我希望在我的类里所有在这个组里的函数都能执行。

Python里有这个功能吗?(我现在在用2.7版本)

3 个回答

1

如果有人对Python 3及以上版本的事件支持感兴趣,可以使用一个叫做event-notifier的库,链接在这里:https://pypi.org/project/event-notifier/

另外,这里还有一个很棒的替代方案列表,可以在这里找到:

15

虽然Jeremy Banks的回答很好用,但大多数人可能不会觉得它是“符合Python风格”的。因为这个问题在搜索引擎上很常见,所以我想分享一个我认为更好的做法:

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

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

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

现在,只需创建一个 EventSource 对象,并通过直接操作 listeners 列表或者使用 += 这个快捷方式来注册监听器回调。然后,你可以使用 emit() 方法来通知所有的监听器。传递给 emit() 的任何参数和关键字参数都会被转发给监听器。

下面是一个完整的例子:

>>> user_registered = EventSource()
>>> def print_user_info(name, age, sex):
...     print(f'Registered {name}, a {age}-year-old {sex}')
...
>>> user_registered += print_user_info
>>> user_registered.emit('Markus', 28, 'male')
Registered Markus, a 23-year-old male

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

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

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

        # Instance level event.
        # Using this you need to listen to each soldier separately.
        self.ate = EventSource()

    def eat(self, amount):
        self.health += amount
        self.ate.emit(self, amount=amount)

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

当然,通常把类和实例级别的事件混合在一起并不是个好主意,我这样做只是为了演示。如果不确定,建议使用实例级别的事件。

16

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

撰写回答