装饰器和类方法

Joe*_* M. 18 python methods class 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__)
Run Code Online (Sandbox Code Playgroud)

现在,运行以下命令:

>>> a = Adder()
>>> a(1)
False
2
>>> a2 = Adder2()
>>> a2(1)    
True
3
Run Code Online (Sandbox Code Playgroud)

我希望这段代码能够打印True两次.

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

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

Mar*_*ers 14

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

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

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

在第二种情况下,但是,您检索方法(同样通过__getattribute__()调用__get__()在未修饰的Adder2.__call__功能),此时未绑定(因为没有实例,只传递给类__get__()(完整的呼叫Adder2.__call__.__get__(None, Adder2)),你装点该方法.现在ismethod()打印True.

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


unu*_*tbu 6

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

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