2009-11-05 14 views
7

Al crear decoradores para usar en métodos de clase, tengo problemas cuando el mecanismo decorador es una clase en lugar de una función/cierre. Cuando se utiliza el formulario de clase, mi decorador no se trata como un método vinculado.Los decoradores de Python en los miembros de la clase fallan cuando el mecanismo decorador es una clase

Generalmente prefiero usar el formulario de función para decoradores, pero en este caso tengo que usar una clase existente para implementar lo que necesito.

Parece que podría estar relacionado con python-decorator-makes-function-forget-that-it-belongs-to-a-class, pero ¿por qué funciona bien para el formulario de función?

Aquí está el ejemplo más simple que podría hacer para mostrar todo lo que sucede. Lo sentimos acerca de la cantidad de código:

def decorator1(dec_param): 
    def decorator(function): 
     print 'decorator1 decoratoring:', function 
     def wrapper(*args): 
      print 'wrapper(%s) dec_param=%s' % (args, dec_param) 
      function(*args) 
     return wrapper 
    return decorator 

class WrapperClass(object): 
    def __init__(self, function, dec_param): 
     print 'WrapperClass.__init__ function=%s dec_param=%s' % (function, dec_param) 
     self.function = function 
     self.dec_param = dec_param 

    def __call__(self, *args): 
     print 'WrapperClass.__call__(%s, %s) dec_param=%s' % (self, args, self.dec_param) 
     self.function(*args) 

def decorator2(dec_param): 
    def decorator(function): 
     print 'decorator2 decoratoring:', function 
     return WrapperClass(function, dec_param) 
    return decorator 

class Test(object): 
    @decorator1(dec_param=123) 
    def member1(self, value=1): 
     print 'Test.member1(%s, %s)' % (self, value) 

    @decorator2(dec_param=456) 
    def member2(self, value=2): 
     print 'Test.member2(%s, %s)' % (self, value) 

@decorator1(dec_param=123) 
def free1(value=1): 
    print 'free1(%s)' % (value) 

@decorator2(dec_param=456) 
def free2(value=2): 
    print 'free2(%s)' % (value) 

test = Test() 
print '\n====member1====' 
test.member1(11) 

print '\n====member2====' 
test.member2(22) 

print '\n====free1====' 
free1(11) 

print '\n====free2====' 
free2(22) 

Salida:

decorator1 decoratoring: <function member1 at 0x3aba30> 
decorator2 decoratoring: <function member2 at 0x3ab8b0> 
WrapperClass.__init__ function=<function member2 at 0x3ab8b0> dec_param=456 
decorator1 decoratoring: <function free1 at 0x3ab9f0> 
decorator2 decoratoring: <function free2 at 0x3ab970> 
WrapperClass.__init__ function=<function free2 at 0x3ab970> dec_param=456 

====member1==== 
wrapper((<__main__.Test object at 0x3af5f0>, 11)) dec_param=123 
Test.member1(<__main__.Test object at 0x3af5f0>, 11) 

====member2==== 
WrapperClass.__call__(<__main__.WrapperClass object at 0x3af590>, (22,)) dec_param=456 
Test.member2(22, 2)  <<<- Badness HERE! 

====free1==== 
wrapper((11,)) dec_param=123 
free1(11) 

====free2==== 
WrapperClass.__call__(<__main__.WrapperClass object at 0x3af630>, (22,)) dec_param=456 
free2(22) 
+1

Aconsejaría el cambio de nombre de la pregunta. En realidad, esto no está realmente relacionado con los decoradores, sino más bien agregar un objeto de función como método de clase – Casebash

+0

Generalmente (aunque no siempre), cuando una pregunta es tan larga, se puede simplificar aislando el problema. Por ejemplo, si hubiera intentado anotar manualmente la clase, se habría dado cuenta de que no tenía nada que ver con decoradores y probablemente hubiera sido más rápido que escribir todo ese código. – Casebash

Respuesta

10

Sus WrapperClass necesario que haya un descriptor (! Al igual que una función es), es decir, el suministro de métodos especiales apropiadas __get__ y __set__. This how-to guide enseña todo lo que necesita saber sobre eso! -)

Cuestiones relacionadas