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

时间:2013-07-25 01:55:51

标签: python

该代码取自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的总和,但在书中,它表明它确实存在! 我对整个代码感到头疼。我不知道这里发生了什么。

2 个答案:

答案 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)

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