如何在Python 2.7中使用装饰器与观察者模式

4 投票
1 回答
1492 浏览
提问于 2025-04-18 11:03

下面的代码展示了观察者模式的一个简单实现,效果很好。我想要一个装饰器 @on_event,它可以在可观察对象的单例中进行注册。

在下面的 O2 类中,这个方法并不好用。问题在于,装饰器 on_event 在实例创建之前就被调用了,这样注册的就是一个未绑定的方法 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")

1 个回答

5

你不能在没有实例的情况下注册绑定方法,也就是说,必须先有一个对象,方法才能绑定到这个对象上。单靠一个函数装饰器是无法知道什么时候创建了这个对象的。

你可以尝试使用元类和装饰器结合的方法:

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 方法会引用这个实例,而因为保持了对这些方法的引用,即使其他对这些实例的引用都被删除了,它们也不会被垃圾回收。

你可以查看 使用 Python WeakSet 来实现回调功能,了解如何使用弱引用,这样就可以在底层实例被删除后,只跟踪方法,而不会让这些实例一直存在。

撰写回答