2010-03-02 120 views
50

这是python 2.5,它的GAE太不重要了。装饰python类方法,如何将实例传递给装饰器?

我有以下代码,我正在装饰bar中的foo()方法,使用dec_check类作为装饰器。

class dec_check(object): 

    def __init__(self, f): 
    self.func = f 

    def __call__(self): 
    print 'In dec_check.__init__()' 
    self.func() 

class bar(object): 

    @dec_check 
    def foo(self): 
    print 'In bar.foo()' 

b = bar() 
b.foo() 

当执行此我希望看到:

In dec_check.__init__() 
In bar.foo() 

但我发现了“TypeError: foo() takes exactly 1 argument (0 given)”为.foo(),作为一个对象的方法,采取自我作为参数。我猜问题是bar的实例在我执行装饰器代码时并不存在。

那么如何将bar的实例传递给装饰类?

回答

70

你需要让装饰成descriptor - 无论是确保其(元)类有一个__get__方法,或者,方式简单,通过使用装饰功能代替修饰器(因为函数已经是描述符)。例如为:

def dec_check(f): 
    def deco(self): 
    print 'In deco' 
    f(self) 
    return deco 

class bar(object): 
    @dec_check 
    def foo(self): 
    print 'in bar.foo' 

b = bar() 
b.foo() 

此打印所期望

In deco 
in bar.foo 

+1

将'f(self)'行更改为'return f(self)'将'foo'的返回值传回给调用者。 – j6m8 2017-07-29 15:32:44

+0

中断描述符页面的链接。 – Apteryx 2017-08-11 15:56:40

43

当功能足够时,Alex的回答就足够了。然而,当你需要一个类时,你可以通过将下面的方法添加到装饰类中来使其工作。

def __get__(self, obj, objtype): 
    """Support instance methods.""" 
    import functools 
    return functools.partial(self.__call__, obj) 

要理解这一点,您需要了解描述符协议。描述符协议是将事物绑定到实例的机制。它由__ get __,__ set __和__ delete __组成,它们在实例字典中获取,设置或删除时调用。在这种情况下,当事件从实例中获得时,我们将它的第一个参数__ call __方法绑定到实例,使用partial。这是在构造类时为成员函数自动完成的,但对于像这样的合成成员函数,我们需要明确地执行它。

+4

这似乎工作。你能介意解释这是如何工作的吗? – Gilbert 2011-02-01 20:10:24

+6

@Gilbert描述符协议是将事物绑定到实例的机制。它由__ get __,__ set __和__ delete __组成,它们在实例字典中获取,设置或删除时调用。在这种情况下,当事件从实例中获得时,我们将它的第一个参数__ call __方法绑定到实例,使用partial。这是在构造类时为成员函数自动完成的,但对于像这样的合成成员函数,我们需要明确地执行它。 – 2011-02-03 23:35:36

+3

那么如果装饰器需要参数呢? – 2012-02-23 00:44:19

0

如果你想写装饰者作为你可以做的类。

from functools import update_wrapper, partial 

MyDecorator(object): 
    def __init__(self, func): 
     update_wrapper(self, func) 
     self.func = func 

    def __get__(self, obj, objtype): 
     """Support instance methods.""" 
     return functools.partial(self.__call__, obj) 

    def __call__(self, obj, *args, **kwargs): 
     print('Logic here') 
     return self.func(obj, *args, **kwargs) 

my_decorator = MyDecorator 

class MyClass(object): 
    @my_decorator 
    def my_method(self): 
     pass