装饰方法

时间:2010-08-03 23:10:18

标签: python methods decorator

在我的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参数。

在Internet上搜索解决方案之后,我尝试过的另一种方法是使用类作为装饰器:

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__的概念,而且我不知道如何合并这些参数。 仅仅为了测试我尝试使用固定事件来收听,但采用这种方法,没有任何反应。当我添加print语句时,我可以看到__init__被调用。 如果我添加了一个额外的“旧式”事件注册,__get____call__都会被执行,尽管有新的装饰器,但事件仍然有效。

实现我正在寻找的东西的最佳途径是什么,或者我只是缺少装饰师的一些重要概念?

2 个答案:

答案 0 :(得分:4)

装饰器方法不起作用,因为在构造类时调用装饰器,而不是在构造实例时调用。当你说

class Foo(object):
  @some_decorator
  def bar(self, *args, **kwargs):
    # etc etc

然后在构造类Foo时将调用some_decorator,并且它将传递未绑定的方法,而不是实例的绑定方法。这就是为什么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.some_method时,系统会调用f ...但您的方案的重点是没有人这样做!事件回调机制直接调用listen实例'因为它传递的是什么,listen实例正在调用它在创建时松开的未绑定方法。永远不会调用listen.__get__并且_self参数永远不会被正确设置... 除非您自己明确访问self.some_method,就像我在上面的__init__方法。然后在创建实例时调用listen.__get__,并正确设置_self

问题是(a)这是一个可怕的,可怕的黑客和(b)如果你试图创建Foo的两个实例,那么第二个将覆盖第一个_self设置,因为仍然只创建了一个listen对象,并且该对象与类相关联,而不是与实例相关联。如果你只使用一个Foo实例,那么你很好,但是如果你必须让事件触发两个不同的Foo,那么你只需要使用你的“旧式”事件注册

TL,DR版本:装饰方法会修饰类的未绑定方法,而您希望事件管理器传递实例的绑定方法。

答案 1 :(得分:3)

您的部分代码是:

    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return func

定义wrapper然后完全忽略它并返回func。很难说这是否是真实代码中的真正问题,因为很明显你没有发布(正如myEventManagremyEvnetManager和& c)这样的错字证明了这一点,但如果这就是你的话在您的实际代码中, 显然是您问题的一部分。