我无法理解为什么会发生以下情况。我有一个装饰器,它除了检查函数是否是方法之外什么都不做。我以为我已经理解了 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 函数进行装饰?
有人可以这么高兴并解释为什么会发生这种情况吗?
最佳答案
方法是与类相关联的函数。仅当您从已定义的类中检索方法时才会创建方法;方法是函数的包装器,同时也引用了类(也可以选择引用实例)。
第一种情况发生的是:Python 编译你的类定义Adder
。它找到装饰器定义和一个函数。装饰器被传递给函数,返回一个 new 函数。该函数被添加到类定义中(存储在类 __dict__
中)。一直以来,您都在处理一个 python 函数,不是一个方法。这会在以后发生。
当您随后调用 a(1)
时,查找显示该实例没有 __call__
但 Adder
类有,因此使用 __getattribute__()
检索它.这会找到一个 function(你的 deco
装饰器),它是一个 descriptor所以它是__get__()
方法被调用(so Adder.__call__.__get__(a, Adder))
,返回一个绑定(bind)的方法,然后在1中调用并传入
值。该方法是绑定(bind)的,因为当调用 __get__()
时 instance
不是 None 。您的装饰器在类构建时包装了一个函数,它打印 False
因为它传递了一个未包装的函数以开始。
然而,在第二种情况下,您检索一个方法(再次通过 __getattribute__()
在未修饰的 Adder2.__call__
上调用 __get__()
> 函数),这次未绑定(bind)(因为没有实例,只有一个类传递给 __get__()
(完整的调用是 Adder2.__call__.__get__(None, Adder2)
),然后你然后装饰那个方法。现在 ismmethod()
打印 True。
请注意,在 Python 3 中,后一种情况发生了变化。在 Python 3 中不再有未绑定(bind)方法的概念,只有函数和绑定(bind)方法。因此,“绑定(bind)”一词被完全删除。您的第二种情况也会打印 False
因为 Adder2.__call__.__get__(None, Adder2)
在这种情况下返回一个函数。
关于python - 装饰器和类方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12493074/