2013-07-25 2 views
6

Код был взят из 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) 

Кроме того, когда я запускаю этот код, он не печатает сумму 1,2,3 либо, но в книга, это показывает, что это так! Я оставил царапины на этом весь код. Я понятия не имею, что здесь происходит.

+0

, что * действительно * происходит? – jheld

ответ

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__ метод перекрываться тело origianlly определено в 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 
+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

Упс! Извините, моя ошибка, я забыл добавить последнюю строку. Тем не менее, я не могу понять этот код. –

Смежные вопросы