2012-07-30 12 views
66

¿Cómo paso un campo de clase a un decorador en un método de clase como argumento? Lo que quiero hacer es algo como:Decodificador de método de clase Python con argumentos propios?

class Client(object): 
    def __init__(self, url): 
     self.url = url 

    @check_authorization("some_attr", self.url) 
    def get(self): 
     do_work() 

Se queja de que yo no existe para pasar self.url al decorador. ¿Hay alguna forma de evitar esto?

+0

¿Es ese un decorador personalizado sobre el que tiene control, o uno que no puede cambiar? –

+0

Es mi decorador, así que tengo control total sobre él – Mark

+0

Se llama antes de init Creo que es el problema ... –

Respuesta

98

Sí. En lugar de pasar en el atributo de instancia en el momento de definición de clase, comprobar en tiempo de ejecución:

def check_authorization(f): 
    def wrapper(*args): 
     print args[0].url 
     return f(*args) 
    return wrapper 

class Client(object): 
    def __init__(self, url): 
     self.url = url 

    @check_authorization 
    def get(self): 
     print 'get' 

>>> Client('http://www.google.com').get() 
http://www.google.com 
get 

El decorador intercepta los argumentos del método; el primer argumento es la instancia, por lo que lee el atributo fuera de eso. Puede pasar en el nombre del atributo como una cadena al decorador y utilizar getattr si no desea codificar el nombre del atributo:

def check_authorization(attribute): 
    def _check_authorization(f): 
     def wrapper(self, *args): 
      print getattr(self, attribute) 
      return f(self, *args) 
     return wrapper 
    return _check_authorization 
2

No puede. No hay self en el cuerpo de la clase, porque no existe ninguna instancia. Tendría que pasarlo, por ejemplo, un str que contenga el nombre del atributo para buscar en la instancia, que la función devuelta puede hacer o utilizar un método diferente por completo.

15
from re import search 
from functools import wraps 

def is_match(_lambda, pattern): 
    def wrapper(f): 
     @wraps(f) 
     def wrapped(self, *f_args, **f_kwargs): 
      if callable(_lambda) and search(pattern, (_lambda(self) or '')): 
       f(self, *f_args, **f_kwargs) 
     return wrapped 
    return wrapper 

class MyTest(object): 

    def __init__(self): 
     self.name = 'foo' 
     self.surname = 'bar' 

    @is_match(lambda x: x.name, 'foo') 
    @is_match(lambda x: x.surname, 'foo') 
    def my_rule(self): 
     print 'my_rule : ok' 

    @is_match(lambda x: x.name, 'foo') 
    @is_match(lambda x: x.surname, 'bar') 
    def my_rule2(self): 
     print 'my_rule2 : ok' 



test = MyTest() 
test.my_rule() 
test.my_rule2() 

ouput: my_rule2: ok

+1

¡Muchas gracias! Esta debería ser la respuesta aceptada. Me estaba arrancando los pelos tratando de descubrir decoradores para los métodos de clase. – benshepherd

+0

@raphael En esta configuración parece que no puedo acceder a _lambda o patrón. ¿Cómo puedo remediar eso? – Jonathan

13

Una más concisa ejemplo podría ser la siguiente:

#/usr/bin/env python3 
from functools import wraps 

def wrapper(method): 
    @wraps(method) 
    def _impl(self, *method_args, **method_kwargs): 
     return method(self, *method_args, **method_kwargs) + "!" 
    return _impl 

class Foo: 
    @wrapper 
    def bar(self, word): 
     return word 

f = Foo() 
result = f.bar("kitty") 
print(result) 

que imprimirá:

kitty! 
Cuestiones relacionadas