装饰方法 [英] Decorating a method

查看:79
本文介绍了装饰方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的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, theselfargument 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆