2013-07-25 38 views
6

的代码已经从Python入门第四版由马克·鲁茨采取可能有人向我解释这个装饰器代码?

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

不*发生什么*? – jheld

回答

7

这里发生的是正在替换的函数的主体。像这样

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

装饰器是等价的:

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

现在,tracer(spam)返回其中spam原来的定义存储在self.func

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

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

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

所以在essense这__call__方法重写身体spam origianlly定义。要对身体进行,你需要调用存储在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 
+0

现在一切都很清晰。谢谢。 :) –

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) 

我不知道他为什么不包含关键字参数的支持,但忽略了,你看,有在那里只有两个额外的行每次调用示踪器实例时都会运行它们。

+0

糟糕!对不起,我的错误,我忘了添加最后一行。不过,我无法理解这段代码。 –

相关问题