2013-07-25 15 views
6

Der Code von Learning Python 4. Auflage von Mark Lutz wurdeKann mir jemand diesen Decorator Code erklären?

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) 

auch genommen, wenn ich diesen Code ausführen, es ist nicht die Summe von 1,2,3 entweder nicht gedruckt, sondern in der Buch, es wird gezeigt, dass es tut! Ich bin bei diesem ganzen Code an meinem Kopf kratzt. Ich habe keine Ahnung, was hier vorgeht.

+0

was * nicht * passieren? – jheld

Antwort

7

Was hier passiert ist der Körper der Funktion wird ersetzt. Ein Dekorateur wie so

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

ist das Äquivalent von:

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

Nun tracer(spam) eine Instanz der tracer Klasse zurückgibt, wo die ursprüngliche Definition von spam in self.func gespeichert

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

Wenn Sie nun spam aufrufen (was eigentlich eine Instanz von tracer ist) Sie berufen sich auf die __call__ Methode in der tracer Klasse definiert:

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

So in essense diese __call__ Methode außer Kraft gesetzt hat der Körper origianlly in spam definiert. Damit der Körper auszuführen, müssen Sie die Funktion in der Instanz der Klasse tracer wie so gespeichert nennen:

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

Resultierende in

>>> 
call 1 to spam 
6 
+0

Jetzt ist alles kristallklar. Vielen Dank. :) –

0

Es ist seltsam, dass Sie nichts mit *args tun. Ist es möglich, dass es eine fehlende Zeile von __call__ gibt?

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) 

Daran erinnern, dass der Dekorateur Syntax ist nur eine andere Art zu sagen

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

so wird Spam eine Instanz des Tracers Klasse und spam in die __init__ Methode übergeben und wird gespeichert self.func

Jetzt, wenn Spam aufgerufen wird, rufen Sie diese Instanz von Tracer auf, so dass die __call__-Methode verwendet wird.

Die nichts gewöhnlich tun __call__ Wrapper ist

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

Ich weiß nicht, warum er die Unterstützung für die Keyword-Argumente nicht aufgenommen hat, aber zu ignorieren, dass Sie sehen, dass es dort nur zwei zusätzliche Zeilen sind in Diese werden jedes Mal ausgeführt, wenn die Tracer-Instanz aufgerufen wird.

+0

Hoppla! Entschuldigung, mein Fehler, ich habe vergessen, die letzte Zeile hinzuzufügen. Trotzdem kann ich diesen Code nicht verstehen. –

Verwandte Themen