2011-05-10 5 views
16

Estoy tratando de entender la decoración decoradores, y quería probar el siguiente:decorar decoradores: tratar de conseguir mi cabeza alrededor de la comprensión de que

Digamos que tengo dos decoradores y aplicarlos a la función hola:

def wrap(f): 
    def wrapper(): 
     return " ".join(f()) 
    return wrapper 


def upper(f): 
    def uppercase(*args, **kargs): 
     a,b = f(*args, **kargs) 
     return a.upper(), b.upper() 
    return uppercase 

@wrap 
@upper 
def hello(): 
    return "hello","world" 

print hello() 

entonces tengo que empezar a añadir otros decoradores para otras funciones, pero en general el decorador envoltura se "envuelva todos ellos"

def lower(f): 
    def lowercase(*args, **kargs): 
     a,b = f(*args, **kargs) 
     return a.lower(), b.lower() 
    return lowercase 

@wrap 
@lower 
def byebye(): 
    return "bye", "bye" 

Ahora, ¿cómo puedo escribir un decorador, con la bruja que puedo decorar mis decoradores inferior y superior:

@wrap 
def lower(): 
    ... 

@wrap 
def upper(): 
    ... 

para lograr el mismo resultado que el anterior sólo haciendo:

@upper 
def hello(): 
    ... 

@lower 
def byebye(): 
    ... 
+1

Probablemente desee leer esto: http://stackoverflow.com/questions/739654/understanding-python-decorators. Hay un ejemplo de decoración de decoradores al final, pero la respuesta lo introduce lentamente. –

Respuesta

6

Aquí es una solución genérica (y un poco complicado) para la decoración de decoradores con decoradores (¡Sí!).

# A second-order decorator 
def decdec(inner_dec): 
    def ddmain(outer_dec): 
     def decwrapper(f): 
      wrapped = inner_dec(outer_dec(f)) 
      def fwrapper(*args, **kwargs): 
       return wrapped(*args, **kwargs) 
      return fwrapper 
     return decwrapper 
    return ddmain 

def wrap(f): 
    def wrapper(): 
     return " ".join(f()) 
    return wrapper 


# Decorate upper (a decorator) with wrap (another decorator) 
@decdec(wrap) 
def upper(f): 
    def uppercase(*args, **kargs): 
     a,b = f(*args, **kargs) 
     return a.upper(), b.upper() 
    return uppercase 

@upper 
def hello(): 
    return "hello","world" 

print hello() 
+0

Esto responde la pregunta, casi. Está decorando un decorador, pero no estaba buscando una solución genérica, en realidad quería un decorador decorado más simple ya que de alguna manera mi cabeza tiene que sortear los argumentos/tareas, no importa la frecuencia con la que repase los ejemplos :) – ashwoods

+0

Bien , puedes hacerlo no genérico, por supuesto. Incluso es bastante simple: escriba 'mywrap = decdec (wrap)' y luego use @mywrap como decorador para decoradores. –

21
def upper(f): 
    @wrap 
    def uppercase(*args, **kargs): 
     a,b = f(*args, **kargs) 
     return a.upper(), b.upper() 
    return uppercase 

Un decorador en Python

@foo 
def bar(...): ... 

es j UST equivalente a

def bar(...): ... 
bar = foo(bar) 

Usted desea conseguir el efecto de

@wrap 
@upper 
def hello(): .... 

es decir

hello = wrap(upper(hello)) 

por lo que el wrap se recurrirá a ellas el valor retorno de upper:

def upper_with_wrap(f): 
    def uppercase(...): ... 
    return wrap(uppercase) 

que también es equivalente a aplicar el decorador en esa función:

def upper_with_wrap(f): 
    @wrap 
    def uppercase(...): ... 
    #^equivalent to 'uppercase = wrap(uppercase)' 
    return uppercase 
+0

esta es una buena solución, pero no es exactamente lo que estaba preguntando, aunque tal vez debería haberlo expresado más claramente en la pregunta. Aunque el uso de esta técnica es más limpio (y más fácil de entender), estaba buscando cómo decorar explícitamente un decorador. Sin embargo, recibes mi voto positivo, ojalá pudiera dar más, especialmente por el tiempo que tardas en explicar tu respuesta :) – ashwoods

Cuestiones relacionadas