该代码取自Mark Lutz的学习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)
此外,当我运行此代码时,它也不会打印1,2,3的总和,但在书中,它表明它确实存在! 我对整个代码感到头疼。我不知道这里发生了什么。
答案 0 :(得分:7)
这里发生的是正在替换功能的主体。像这样的装饰者
@tracer
def spam(...)
...
相当于:
def spam(...)
...
spam = tracer(spam)
现在,tracer(spam)
会返回tracer
类的一个实例,其中spam
的原始定义存储在self.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
答案 1 :(得分: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
传递到__init__
方法并保存到self.func
现在,当调用垃圾邮件时,您正在调用该跟踪器实例,因此使用了__call__
方法。
通常不做任何事情__call__
包装器
def __call__(self, *args, **kw):
return self.func(*args, **kw)
我不知道为什么他没有包含对关键字参数的支持,但忽略了这一点,你会发现每次调用跟踪器实例时都会有两行额外的行。