分类目录:《系统学习Python》总目录


前文基于类的代码的微妙之处是,尽管它对于拦截简单函数调用有效,但当它应用于类级别的方法函数的时候,并不是很有效:

def decorator(F):
    def __init__(self, func):
        self.func = func
    def __call__(self, *args):
        pass    # 使用self.func和*args

class A():
    @decorator
    def method(self, x, y):    # F = decorator(F)
        pass

这一点带来的问题是,当装饰器__call__方法随后运行的时候,其中的self接收decorator类实例,并且类A的实例不会包含到一个args中。这使得把调用分派给最初的方法变得不可能,即保持了最初的方法函数的装饰器对象,但是没有实例传递给它。

为了支持函数和方法,嵌套函数这一替代方案将有更好的效果:

def decorator(F):
    def wrapper(*args):

        return wrapper

@decorator
def func(x, y):
    pass

func(1, 1)

class A():
    @decorator
    def method(self, x, y):    # F = decorator(F)
        pass

X = A()
X.method(1, 1)

当按照这种方法编写时,wrapper在其第一个参数里接收了类的实例,因此它可以分发到最初的方法和访问状态信息。

从技术上讲,这种嵌套函数版本是有效的,因为Python创建了一个绑定的方法对象,并且只有当一个方法属性引用简单函数的时候,才把主体类实例传递给该方法的self参数;当方法引用可调用类的一个实例时,将可调用类的实例传递给self参数,以允许可调用类访问自己的状态信息。

我们在后续的文章中还将看到这一细微的区别在实际案例中的作用。还需要注意的是,嵌套函数可能是支持函数和方法装饰的最直接方式,但是不一定是唯一方式。例如,之前的文章中提到的描述符,调用的时候接收了描述符和主体类实例。

参考文献:
[1] Mark Lutz. Python学习手册[M]. 机械工业出版社, 2018.