愿有人向我解释这个装饰代码吗?

Alw*_*ver 6 python

该代码取自Mark Lutz的Learning Python第4版

class tracer:
            def __init__(self, func):
               self.calls = 0
               self.func = func
            def __call__(self, *args):
               self.calls += 1
               print('call %s to %s' % (self.calls, self.func.__name__))
               self.func(*args)


@tracer

def spam(a, b, c):
    print(a + b + c)

spam(1, 2, 3)
Run Code Online (Sandbox Code Playgroud)

此外,当我运行此代码时,它也不会打印1,2,3的总和,但在书中,它表明它确实如此!我对整个代码感到头疼.我不知道这里发生了什么.

Hen*_*nyH 7

这里发生的是功能的主体正在被替换.像这样的装饰师

@tracer
def spam(...)
   ...
Run Code Online (Sandbox Code Playgroud)

相当于:

def spam(...)
   ...
spam = tracer(spam)
Run Code Online (Sandbox Code Playgroud)

现在,tracer(spam)返回存储tracer原始定义的类的实例spamself.func

class tracer:
            def __init__(self, func):  #tracer(spam), func is assigned spam
               self.calls = 0
               self.func = func
Run Code Online (Sandbox Code Playgroud)

现在,当您调用spam(实际上是其实例tracer)时,您将调用类中__call__定义的方法tracer:

def __call__(self, *args):
   self.calls += 1
   print('call %s to %s' % (self.calls, self.func.__name__))
Run Code Online (Sandbox Code Playgroud)

所以在本质中,这种__call__方法已经覆盖了原始定义的体spam.要使主体执行,您需要调用存储在tracer类实例中的函数,如下所示:

def __call__(self, *args):
               self.calls += 1
               print('call %s to %s' % (self.calls, self.func.__name__))
               self.func(*args)
Run Code Online (Sandbox Code Playgroud)

导致

>>> 
call 1 to spam
6
Run Code Online (Sandbox Code Playgroud)