在我的 Python 应用程序中,我使用事件在不同的插件之间进行通信。 现在,与其手动将方法注册到事件,我想我可以使用装饰器来为我做这件事。
我希望它看起来像这样:
@events.listento('event.name')
def myClassMethod(self, event):
...
我首先尝试这样做:
def listento(to):
def listen_(func):
myEventManager.listen(to, func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return func
return listen_
当我从实例中调用myEventManger.listen('event', self.method)
时,一切都运行良好。但是,如果我使用装饰器方法,则永远不会传递self
参数。
我尝试过的另一种方法是在网上搜索解决方案后,使用一个类作为装饰器:
class listen(object):
def __init__(self, method):
myEventManager.listen('frontend.route.register', self)
self._method = method
self._name = method.__name__
self._self = None
def __get__(self, instance, owner):
self._self = instance
return self
def __call__(self, *args, **kwargs):
return self._method(self._self, *args, **kwargs)
这种方法的问题在于我并不真正理解__get__
的概念,而且我不知道如何合并这些参数。
只是为了测试,我尝试使用固定事件来收听,但是使用这种方法没有任何反应。当我添加打印语句时,我可以看到 __init__
被调用了。
如果我添加一个额外的“旧式”事件注册,__get__
和 __call__
都会被执行,并且事件会正常运行,尽管有新的装饰器。
实现我正在寻找的东西的最佳方法是什么,或者我只是错过了装饰器的一些重要概念?
最佳答案
装饰器方法不起作用,因为装饰器是在构造类时调用的,而不是在构造实例时调用的。当你说
class Foo(object):
@some_decorator
def bar(self, *args, **kwargs):
# etc etc
然后 some_decorator
将在构造类 Foo 时被调用,并且将传递一个未绑定(bind) 方法,而不是实例的绑定(bind)方法。这就是 self
没有通过的原因。
另一方面,只要您只为使用装饰器的每个类创建一个对象,第二种方法就可以工作,并且如果您你有点聪明。如果你像上面那样定义listen
然后定义
class Foo(object):
def __init__(self, *args, **kwargs):
self.some_method = self.some_method # SEE BELOW FOR EXPLANATION
# etc etc
@listen
def some_method(self, *args, **kwargs):
# etc etc
然后 listen.__get__
会在有人试图直接为某些 f
调用 f.some_method
时被调用...但整点你的计划是没有人这样做!事件回调机制直接调用 listen
实例,因为这是它传递的内容,listen
实例正在调用它在创建时隐藏起来的未绑定(bind)方法。 listen.__get__
永远不会被调用并且 _self
参数永远不会正确设置...除非您显式访问 self .some_method
自己,就像我在上面的 __init__
方法中所做的那样。然后 listen.__get__
将在实例创建时调用,并且 _self
将被正确设置。
问题是 (a) 这是一个可怕的骇人听闻的 hack 和 (b) 如果您尝试创建 Foo
的两个实例,那么第二个实例将覆盖 _self
由第一个设置,因为仍然只有一个 listen
对象被创建,并且它与类关联,而不是实例。如果你只使用一个 Foo
实例那么你很好,但是如果你必须让事件触发两个不同的 Foo
那么你只需要使用您的“旧式”事件注册。
TL,DR 版本:装饰方法装饰类的未绑定(bind) 方法,而您希望事件管理器通过实例的绑定(bind) 方法。
关于python - 装饰方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3401421/