装饰器和类方法

时间:2012-09-19 10:41:29

标签: python class methods decorator python-decorators

我无法理解为什么会发生以下情况。我有一个装饰器,它除了检查函数是否是一个方法之外什么都不做。我以为我已经理解了Python中的哪种方法,但显然情况并非如此:

import inspect

def deco(f):
    def g(*args):
        print inspect.ismethod(f)
        return f(*args)
    return g

class Adder:
    @deco
    def __call__(self, a):
        return a + 1

class Adder2:
    def __call__(self, a):
        return a + 2
Adder2.__call__ = deco(Adder2.__call__)

现在,运行以下内容:

>>> a = Adder()
>>> a(1)
False
2
>>> a2 = Adder2()
>>> a2(1)    
True
3

我希望此代码能够两次打印True。

那么,在Adder2中手动装饰功能与通过@deco功能进行装饰并不完全相同?

有人可以这么高兴并解释为什么会这样吗?

2 个答案:

答案 0 :(得分:14)

方法是与类关联的函数。只有从已定义的类中检索方法时才会创建方法;方法是函数的包装器,同时引用类(以及可选的实例引用)。

第一种情况会发生什么:Python编译您的类定义Adder。它找到装饰器定义和函数。装饰器传递给函数,返回 new 函数。该函数被添加到类定义中(存储在类__dict__中)。所有这段时间你都在处理一个python函数,不是一个方法。这发生在以后。

当您再调用a(1)时,查找显示该实例没有__call__Adder类没有,因此使用__getattribute__()进行检索。这找到了一个函数(你的deco装饰器),这是一个descriptor所以它的__get__()方法被调用(所以Adder.__call__.__get__(a, Adder)),返回一个< em>绑定方法,然后在1值中调用并传递。该方法被绑定,因为instance在调用__get__()时不是None。你的装饰器,在类构建时包装了一个函数,打印False,因为它是以一个未打开的函数传递的。

然而,在第二种情况下,您检索一个方法(再次通过__getattribute__()在未修饰的__get__()函数上调用Adder2.__call__),这次是未绑定的(因为没有实例,仅传递给__get__()的一个类(完整调用是Adder2.__call__.__get__(None, Adder2)),然后然后装饰该方法。现在ismethod()打印为True。

请注意,在Python 3中,后一种情况会发生变化。在Python 3中,不再存在未绑定方法的概念,只有函数和绑定方法。因此,“绑定”一词完全被删除。第二种情况也会打印False,因为Adder2.__call__.__get__(None, Adder2)会返回一个函数。

答案 1 :(得分:6)

在类定义中,__call__是一个函数,而不是一个方法。通过属性查找(例如使用点语法)访问函数的行为(例如使用Adder2.__call__)返回未绑定的方法。 a2.__call__返回绑定方法(self绑定到a2)。

请注意,在Python3中,unbound method的概念已被删除。在那里,Adder2.__call__也是一个功能。