2010-03-02 3 views
50

Dies ist Python 2.5, es ist GAE auch nicht, dass es darauf ankommt.Python-Klassen-Methoden dekorieren, wie übertrage ich die Instanz an den Dekorator?

Ich habe den folgenden Code, ich dekoriere die foo() -Methode in bar, mit der Dec_check-Klasse als Dekorateur.

class dec_check(object): 

    def __init__(self, f): 
    self.func = f 

    def __call__(self): 
    print 'In dec_check.__init__()' 
    self.func() 

class bar(object): 

    @dec_check 
    def foo(self): 
    print 'In bar.foo()' 

b = bar() 
b.foo() 

Wenn diese Ausführung Ich hatte gehofft, um zu sehen:

In dec_check.__init__() 
In bar.foo() 

Aber ich bin immer „TypeError: foo() takes exactly 1 argument (0 given)“ als .foo(), eine Objektmethode zu sein, nimmt sich selbst als Argument. Ich vermute, dass die Instanz bar tatsächlich nicht existiert, wenn ich den Decorator-Code ausführe.

Wie also gebe ich eine Instanz von bar an die Dekorator-Klasse?

Antwort

70

Sie müssen den Dekorateur in einem descriptor machen - entweder durch ihre Gewährleistung (meta) Klasse hat eine __get__ Methode oder, Weise einfacher, ein Dekorateur Funktion anstelle eines Dekorateur Klasse (da Funktionen bereits Deskriptoren sind). Z.B .:

def dec_check(f): 
    def deco(self): 
    print 'In deco' 
    f(self) 
    return deco 

class bar(object): 
    @dec_check 
    def foo(self): 
    print 'in bar.foo' 

b = bar() 
b.foo() 

diese druckt

In deco 
in bar.foo 

nach Wunsch.

+1

ändern Sie die 'f (Selbst-)' Zeile zu 'return f (Selbst-)' die Rückkehr von 'foo' passiert zurück an den Anrufer. – j6m8

+0

Link zur Deskriptor-Seite unterbrochen. – Apteryx

43

Die Antwort von Alex reicht aus, wenn eine Funktion ausreicht. Wenn Sie jedoch eine Klasse benötigen, können Sie sie durch Hinzufügen der folgenden Methode zur Decorator-Klasse verbessern.

def __get__(self, obj, objtype): 
    """Support instance methods.""" 
    import functools 
    return functools.partial(self.__call__, obj) 

Um dies zu verstehen, müssen Sie das Deskriptorprotokoll verstehen. Das Deskriptorprotokoll ist der Mechanismus zum Binden einer Sache an eine Instanz. Es besteht aus __ get __, __ set __ und __ delete __, die aufgerufen werden, wenn das Ding aus dem Instanzenwörterbuch abgerufen, gesetzt oder gelöscht wird. In diesem Fall binden wir, wenn das Ding von der Instanz kommt, das erste Argument seiner Methode __ call __ an die Instanz, wobei partially verwendet wird. Dies geschieht automatisch für Member-Funktionen, wenn die Klasse konstruiert wird, aber für eine synthetische Member-Funktion wie diese müssen wir dies explizit tun.

+4

Das scheint zu funktionieren. Würde es Ihnen etwas ausmachen zu erklären, wie das funktioniert? – Gilbert

+6

@Gilbert Das Deskriptor-Protokoll ist der Mechanismus zum Binden einer Sache an eine Instanz. Es besteht aus __ get __, __ set __ und __ delete __, die aufgerufen werden, wenn das Ding aus dem Instanzenwörterbuch abgerufen, gesetzt oder gelöscht wird.In diesem Fall binden wir, wenn das Ding von der Instanz kommt, das erste Argument seiner Methode __ call __ an die Instanz, wobei partially verwendet wird. Dies geschieht automatisch für Member-Funktionen, wenn die Klasse konstruiert wird, aber für eine synthetische Member-Funktion wie diese müssen wir dies explizit tun. –

+3

Also, was ist, wenn der Dekorateur Argumente nimmt? –

0

Wenn Sie den Dekorateur als eine Klasse schreiben möchten, können Sie tun.

from functools import update_wrapper, partial 

MyDecorator(object): 
    def __init__(self, func): 
     update_wrapper(self, func) 
     self.func = func 

    def __get__(self, obj, objtype): 
     """Support instance methods.""" 
     return functools.partial(self.__call__, obj) 

    def __call__(self, obj, *args, **kwargs): 
     print('Logic here') 
     return self.func(obj, *args, **kwargs) 

my_decorator = MyDecorator 

class MyClass(object): 
    @my_decorator 
    def my_method(self): 
     pass 
Verwandte Themen