【问题标题】:How to use decorator in observer pattern for Python 2.7如何在 Python 2.7 的观察者模式中使用装饰器
【发布时间】:2026-01-08 12:45:02
【问题描述】:

下面非常简化的代码中的观察者模式运行良好。我希望有一个装饰器@on_event 在 Observable 单例中进行注册。

在下面的 O2 类中,这不起作用。问题当然是在创建实例之前调用了装饰器on_event get,并且注册将是未绑定方法event。在某种程度上,我必须延迟注册,直到 O2 对象被初始化。也许不用说,但我想在 O2 中添加的只是装饰器,如下面的代码所示。

但肯定有办法解决这个问题吗?我用谷歌搜索但找不到任何东西,并尝试了几种方法。

class Observable(object):
    _instance = None

    @classmethod
    def instance(cls):
        if not cls._instance:
            cls._instance = Observable()
        return cls._instance

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

    def event(self, data):
        for fn in self.obs:
            fn(data)

def on_event(f):
    def wrapper(*args):
        return f(*args)
    Observable.instance().obs.append(f)
    return wrapper

class O(object):

    def __init__(self, name):
        self.name = name
        Observable.instance().obs.append(self.event)

    def event(self, data):
        print self.name + " Event: " + data

class O2(object):

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

    @on_event
    def eventx(self, data):
        print self.name + " Event: " + data

if __name__ == "__main__":
    o1 = O("o1")
    Observable.instance().event("E1")

    o2 = O2("o2")
    Observable.instance().event("E2")

【问题讨论】:

  • 那么,当事件触发时,该方法应该调用什么instance?这就是这里的问题;如果有实例将方法绑定到,则只能注册绑定方法。你想在这里注册什么?只有一个O2实例的方法,O2的所有实例,还有什么?
  • @MartijnPieters 在示例中应该在 o2 实例上调用 - 所有 O2 实例。 (就像 O 一样)
  • 你不能单独使用装饰器来做到这一点;您还需要在O2 上设置一个元类以挂钩实例创建,然后在每次创建实例时注册绑定方法。
  • 虽然理论上装饰器也可以将__new____init__ 方法插入到同一个类命名空间中,然后进行挂钩;但这会很快变得丑陋,不推荐。
  • (请参阅 How does the function that is called inside the class declaration? 了解这有多难看;该特定技巧甚至在 Python 3 中都行不通)。

标签: python python-2.7 decorator python-decorators


【解决方案1】:

在您拥有要绑定的方法的实例之前,您不能注册绑定的方法。单独的函数装饰器没有上下文来检测实例何时被创建。

您可以改用元类/装饰器组合方法:

class ObservingMeta(type):
    def __call__(cls, *args, **kw):
         instance = super(ObservingMeta, cls).__call__(*args, **kw)
         for attr in vars(cls).values():
             if hasattr(attr, '__observer__'):
                 # register bound method
                 bound = attr.__get__(instance, cls)
                 Observable.instance().obs.append(bound)
         return instance

这会直接注册在cls上定义的所有标记为观察者的方法;标记是用装饰器完成的:

def on_event_method(f):
    f.__observer__ = True
    return f

然后将其用作:

class O2(object):
    __metaclass__ = ObservingMeta

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

    @on_event_method
    def eventx(self, data):
        print self.name + " Event: " + data

请注意,将方法存储在 Observable 单例中可以使实例保持活动状态;如果您创建 O2 的实例,则绑定的 eventx 方法会引用该实例,并且通过保留对方法的引用,这意味着如果删除对它们的所有其他引用,则永远不会对实例进行垃圾回收。

请参阅using python WeakSet to enable a callback functionality,了解如何使用弱引用来仅在删除底层实例之前密切关注方法,而不是无限期地保持实例活动。

【讨论】:

  • 这个解决方案解决了问题中提到的问题,但是我真的想要一个解决方案,在模块级别也可以使用相同的装饰器。原因是问题在于装饰器没有在课堂上工作。
  • 是的,但这需要检测您是否在类定义中并调整您注册函数的方式。见*.com/questions/8793233/…。再一次,相当丑陋,而且 任何 函数都可以变成一个方法,真的。