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

时间:2014-06-25 16:07:31

标签: python python-2.7 decorator python-decorators

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

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

但是肯定必须有解决方案吗?我用Google搜索但找不到任何东西,并尝试了几种方法。

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 个答案:

答案 0 :(得分: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方法引用该实例,并通过保留对方法的引用来表示如果删除了对它们的所有其他引用,则实例永远不会被垃圾回收。

请参阅using python WeakSet to enable a callback functionality,了解如何使用弱引用代替只保留方法,直到基础实例被删除,而不会无限期地保持实例生效。