2009-03-20 12 views
34

Quiero que construya clases para su uso como decoradores con los siguientes principios intactos:clases de decorador en Python

  1. Debería ser posible apilar múltiples tales decoradores de clase 1 en la parte superior de la función.
  2. El puntero del nombre de la función resultante no se puede distinguir de la misma función sin un decorador, excepto tal vez para qué tipo/clase es.
  3. Ordenar fuera de los decoradores no debería ser relevante a menos que realmente sea obligatorio por los decoradores. Es decir. decoradores independientes podrían ser aplicados en cualquier orden.

Esto es para un proyecto de Django, y el caso específico que estoy trabajando ahora el método de Necesidades 2 decoradores, y para que aparezca como una función normal de pitón:

@AccessCheck 
@AutoTemplate 
def view(request, item_id) {} 

@AutoTemplate cambia la función de modo que en lugar de devolver un HttpResponse, simplemente devuelve un diccionario para usar en el contexto. Se utiliza un RequestContext y el nombre de la plantilla se deduce del nombre y el módulo del método.

@AccessCheck agrega controles adicionales en el usuario en función del item_id.

Supongo que es solo para obtener el constructor correcto y copiar los atributos apropiados, pero ¿qué atributos son estos?

La siguiente decorador no funcionará como se describo:

class NullDecl (object): 
    def __init__ (self, func): 
     self.func = func 
    def __call__ (self, * args): 
     return self.func (*args) 

como lo demuestra el siguiente código:

@NullDecl 
@NullDecl 
def decorated(): 
    pass 

def pure(): 
    pass 

# results in set(['func_closure', 'func_dict', '__get__', 'func_name', 
# 'func_defaults', '__name__', 'func_code', 'func_doc', 'func_globals']) 
print set(dir(pure)) - set(dir(decorated)); 

Además, tratar de añadir "func impresión nombre." En el constructor NullDecl, y funcionará para el primer decorador, pero no el segundo, ya que faltará el nombre.

refinado eduffy 's contestar un poco, y parece que funciona bastante bien: clase decoradora

class NullDecl (object): 
    def __init__ (self, func): 
     self.func = func 
     for n in set(dir(func)) - set(dir(self)): 
      setattr(self, n, getattr(func, n)) 

    def __call__ (self, * args): 
     return self.func (*args) 
    def __repr__(self): 
     return self.func 
+1

+1 porque' No estoy seguro de por qué alguien votó esta pregunta. Es una buena pregunta: la mayoría de las preguntas de decorador sobre SO no usan clases como decoradores. Cualquier cosa que uno piense utiliza una clase en lugar de una función, es una pregunta válida y puede ser completamente apropiada para el caso de uso. –

Respuesta

21

A que no hace nada se vería así:

class NullDecl (object): 
    def __init__ (self, func): 
     self.func = func 
     for name in set(dir(func)) - set(dir(self)): 
     setattr(self, name, getattr(func, name)) 

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

Y entonces puede aplicarlo normalmente:

@NullDecl 
def myFunc (x,y,z): 
    return (x+y)/z 
+0

Consulte mi comentario sobre por qué su código no funcionará como dije. – Staale

+0

Agregué mi propia corrección para que funcione correctamente. Gracias por la ayuda, generalmente ayuda a comenzar muy básico. – Staale

+6

Quizás también haya un lugar para agregar '** kwargs' a la definición' def __call__ (self, * args): '(y a la invocación de' self.func' más adelante)? –

7

Para crear un decorador que ajuste las funciones en un asunto que las haga indistinguibles de la función original, use functools.wraps.

Ejemplo:


def mydecorator(func): 
    @functools.wraps(func): 
    def _mydecorator(*args, **kwargs): 
     do_something() 
     try: 
      return func(*args, **kwargs) 
     finally: 
      clean_up() 
    return _mydecorator 

# ... and with parameters 
def mydecorator(param1, param2): 
    def _mydecorator(func): 
     @functools.wraps(func) 
     def __mydecorator(*args, **kwargs): 
      do_something(param1, param2) 
      try: 
       return func(*args, **kwargs) 
      finally: 
       clean_up() 
     return __mydecorator 
    return _mydecorator 

(mi preferencia personal es crear decoradores mediante funciones, no clases)

El orden de los decoradores es el siguiente:


@d1 
@d2 
def func(): 
    pass 

# is equivalent to 
def func(): 
    pass 

func = d1(d2(func)) 
+0

Prefiero usar clases, pero supongo que se debe a mi fondo de Java. Creo que leí a alguien recomendando el uso de clases para decoradores, pero supongo que fue un mal consejo. – Staale

+2

Si desea utilizar clases, no estoy seguro de dónde pone @ functools.wraps. Siento que las funciones como decoradores son más pitónicas. Y el código es más corto. – codeape

+0

En su ejemplo de parámetros, ¿de dónde viene "func"? No está en la lista de parámetros de mydecorator() ... –

Cuestiones relacionadas