2010-03-02 10 views
50

Esto es python 2.5, es GAE también no es que importe.Decorando métodos de clase de python, ¿cómo paso la instancia al decorador?

Tengo el siguiente código, estoy decorando el método foo() en la barra, usando la clase dec_check como decorador.

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() 

Cuando se ejecuta esta Tenía la esperanza de ver:

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

Pero estoy "TypeError: foo() takes exactly 1 argument (0 given)" como .foo(), siendo un método de objeto, toma a sí mismo como un argumento. Supongo que el problema es que la instancia de bar no existe cuando estoy ejecutando el código del decorador.

Entonces, ¿cómo paso una instancia de bar a la clase de decorador?

Respuesta

70

que necesita para hacer el decorador en una descriptor - asegurando su (meta) de clase tiene un método __get__, o, manera más simple, mediante el uso de un decorador función en lugar de una decorador clase (ya que las funciones ya son descriptores). Ej .:

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() 

este imprime

In deco 
in bar.foo 

como se desee.

+1

Cambie la línea 'f (self)' a 'return f (self)' para pasar la devolución de 'foo' de vuelta a la persona que llama. – j6m8

+0

Enlace roto a la página del descriptor. – Apteryx

43

La respuesta de Alex es suficiente cuando una función es suficiente. Sin embargo, cuando necesite una clase, puede hacerlo funcionar agregando el siguiente método a la clase de decorador.

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

Para entender esto, debe comprender el protocolo del descriptor. El protocolo descriptor es el mecanismo para vincular una cosa a una instancia. Consiste en __ get __, __ set __ y __ delete __, que se invocan cuando la cosa se obtiene, se establece o se elimina del diccionario de instancias. En este caso, cuando la cosa se obtiene de la instancia, estamos vinculando el primer argumento de su método __ call __ a la instancia, usando partial. Esto se hace automáticamente para las funciones miembro cuando se construye la clase, pero para una función sintética de miembro como esta, tenemos que hacerlo explícitamente.

+4

Esto parece funcionar. ¿Te importaría explicar cómo funciona esto? – Gilbert

+6

@Gilbert El protocolo de descriptor es el mecanismo para vincular una cosa a una instancia. Consiste en __ get __, __ set __ y __ delete __, que se invocan cuando la cosa se obtiene, se establece o se elimina del diccionario de instancias.En este caso, cuando la cosa se obtiene de la instancia, estamos vinculando el primer argumento de su método __ call __ a la instancia, usando parcial. Esto se hace automáticamente para las funciones miembro cuando se construye la clase, pero para una función sintética de miembro como esta, tenemos que hacerlo explícitamente. –

+3

¿Y si el decorador toma argumentos? –

0

si desea escribir el decorador como una clase que puede hacer.

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 
Cuestiones relacionadas