如何在Python 2.7中使用装饰器与观察者模式
下面的代码展示了观察者模式的一个简单实现,效果很好。我想要一个装饰器 @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 来实现回调功能,了解如何使用弱引用,这样就可以在底层实例被删除后,只跟踪方法,而不会让这些实例一直存在。