我无法理解为什么会发生以下情况。我有一个装饰器,它除了检查函数是否是一个方法之外什么都不做。我以为我已经理解了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功能进行装饰并不完全相同?
有人可以这么高兴并解释为什么会这样吗?
答案 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__
也是一个功能。