装饰方法 [英] Decorating a method
问题描述
在我的Python应用中,我正在使用事件在不同的插件之间进行通信。
现在,我不想使用手动将方法注册到事件中,而是想用装饰器为我做这件事。
In my Python app, I'm using events to communicate between different plugins. Now, instead of registering the methods to the events manually, I thought I might use decorators to do that for me.
我想让它看起来像像这样:
I would like to have it look like this:
@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)
,一切运行正常。但是,如果我使用装饰器方法,则自身
参数将永远不会传递。
When I callmyEventManger.listen('event', self.method)
from within the instance, everything is running fine. However, if I use the decorator approach, theself
argument is never passed.
我拥有的另一种方法在Internet上寻找解决方案后,尝试将类用作装饰器:
The other approach that I have tried, after searching for a solution on the Internet, is to use a class as a decorator:
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 __
都将执行,
The problem with this approach is that I don't really understand the concept of__get__
, and that I don't know how I'd incorporate the parameters.
Just for testing I have tried using a fixed event to listen to, but with this approach, nothing happens. When I add print statements, I can see that__init__
is called.
If I add an additional, "old style" event registration, both__get__
and__call__
get executed, and the event works, despite the new decorator.
什么是实现我所寻找的最佳方法,或者我只是缺少一些重要的装饰概念? ?
What would be the best way to achieve what I'm looking for, or am I just missing some important concept with decorators?
推荐答案
装饰器方法不起作用,因为在构造类时调用装饰器,而不是在实例创建时调用装饰器。建。当你说
The decorator approach isn't working because the decorator is being called when the class is constructed, not when the instance is constructed. When you say
class Foo(object):
@some_decorator
def bar(self, *args, **kwargs):
# etc etc
然后 some_decorator
在构造Foo类时将被调用,并且将被传递 unbound 方法,而不是实例的bound方法。这就是为什么 self
无法通过的原因。
then some_decorator
will be called when the class Foo is constructed, and it will be passed an unbound method, not the bound method of an instance. That's why self
isn't getting passed.
另一方面,第二种方法可以工作很长时间因为您只会在每个类中创建一个一个对象,所以您可以在和上使用修饰符,如果您比较聪明的话。如果您如上所述定义 listen
,然后定义
The second method, on the other hand, could work as long as you only ever create one object of each class you use the decorator on, and if you're a bit clever. If you define listen
as above and then define
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
然后,当有人尝试直接为 f.some_method
时,会调用 listen .__ get __
。 > f ...但是您计划的全部要点是没有人这样做!事件回调机制直接调用 listen
实例,因为这就是它传递的内容,而 listen
实例正在调用创建它时松散的未绑定方法。 listen .__ get __
永远不会被调用,并且 _self
参数永远不会正确设置... 除非,否则您自己会显式访问 self.some_method
,就像我在上述 __ init __
方法中所做的那样。然后,在实例创建时将调用 listen .__ get __
并正确设置 _self
。
Then listen.__get__
would be called when someone tried to call f.some_method
directly for some f
...but the whole point of your scheme is that no-one's doing that! The event call back mechanism is calling the listen
instance directly 'cause that's what it gets passed and the listen
instance is calling the unbound method it squirrelled away when it was created. listen.__get__
won't ever get called and the _self
parameter is never getting set properly...unless you explicitly access self.some_method
yourself, as I did in the __init__
method above. Then listen.__get__
will be called upon instance creation and _self
will be set properly.
问题是(a)这是一个可怕的骇客,并且(b)如果您尝试创建两个 Foo
实例,则第二个实例将覆盖第一个设置的 _self
,因为仍然只创建了一个 listen
对象,并且该对象与类,而不是实例。如果只使用一个 Foo
实例,那很好,但是如果必须让该事件触发两个不同的 Foo
,那么您只需要使用旧式事件注册。
Problem is (a) this is a horrible, horrible hack and (b) if you try to create two instances of Foo
then the second one will overwrite the _self
set by the first, because there's still only one listen
object being created, and that's associated to the class, not the instance. If you only ever use one Foo
instance then you're fine, but if you have to have the event trigger two different Foo
's then you'll just have to use your "old style" event registration.
TL,DR版本:装饰方法装饰了 unbound 类,而您希望事件管理器传递实例的 bound 方法。
The TL,DR version: decorating a method decorates the unbound method of the class, whereas you want your event manager to get passed the bound method of an instance.
这篇关于装饰方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!