6

代码取自 Mark Lutz 的 Learning Python 4th Edition

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)

此外,当我运行此代码时,它也不会打印 1、2、3 的总和,但在书中,它显示它确实如此!整个代码让我摸不着头脑。我不知道这里发生了什么。

4

2 回答 2

7

这里发生的是函数的主体被替换。像这样的装饰师

@tracer
def spam(...)
   ...

相当于:

def spam(...)
   ...
spam = tracer(spam)

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

class tracer:
            def __init__(self, func):  #tracer(spam), func is assigned spam
               self.calls = 0
               self.func = func

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

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

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

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

导致

>>> 
call 1 to spam
6
于 2013-07-25T02:03:19.543 回答
0

奇怪的是你没有对*args. 是否有可能缺少一行__call__

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__))
       return self.func(*args)

回想一下,装饰器语法只是表达这一点的另一种方式

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

所以 spam 成为tracer 类的一个实例spam,并被传递到__init__方法中并保存到self.func

现在,当调用 spam 时,您正在调用该跟踪器实例,因此使用该__call__方法。

通常什么都不做的__call__包装是

def __call__(self, *args, **kw):
    return self.func(*args, **kw)

我不知道他为什么不包含对关键字参数的支持,但忽略这一点,您会看到每次调用跟踪器实例时都会运行两行额外的行。

于 2013-07-25T02:00:48.313 回答