2011-03-23 29 views

Respuesta

39

Un poco más general:

def composed(*decs): 
    def deco(f): 
     for dec in reversed(decs): 
      f = dec(f) 
     return f 
    return deco 

Entonces

@composed(dec1, dec2) 
def some(f): 
    pass 

es equivalente a

@dec1 
@dec2 
def some(f): 
    pass 
+1

[No hay espacios dentro de parens.] (Http://www.python.org/dev/peps/pep-0008/) – delnan

+1

'return lambda x: reduce (lambda y, f: f (y), decs, x) '... bueno, después de tipear esto veo la ventaja de tu código :) –

+1

Otra cosa que acabo de notar:' @composed (dec1, dec2) 'será equivalente a' @ dec2 @ dec1 ', que es al menos contrario a la intuición. –

3

Si los decoradores no toman argumentos adicionales, usted podría utilizar

def compose(f, g): 
    return lambda x: f(g(x)) 

combined_decorator = compose(decorator1, decorator2) 

Ahora

@combined_decorator 
def f(): 
    pass 

será equivalente a

@decorator1 
@decorator2 
def f(): 
    pass 
+0

¿No es que "aplicar múltiples decoradores a una función"? – delnan

+0

@delnan: ¿No es "una forma simple de combinar dos [decoradores] en uno nuevo"? :) –

+0

Gracias. Uno de los decoradores toma params aunque así ha ido con la otra respuesta. – Ludo

17

Sí. Consulte la definición de decorador, here.

Algo como esto debería funcionar:

def multiple_decorators(func): 
    return decorator1(decorator2(func)) 

@multiple_decorators 
def foo(): pass 
+0

Gracias, y también un enlace útil. Opté por la respuesta con la solución más general. Aclamaciones. – Ludo

+0

Me gusta lo conciso que es esta solución y la he encontrado útil en mi proyecto. –

+0

** Ídem. ** Aunque la respuesta aceptada es ciertamente admirable para el caso general, esta respuesta muestra de manera concisa un decorador difiriendo a muchos otros decoradores cuyos nombres se conocen estáticamente en el momento de la interpretación. Como este es el caso habitual, ¡esto también es genial! _Upvotes para todos subsue._ –

1

Si no, quiero que repetir demasiado en un banco de pruebas, se podría hacer como esto ::

def apply_patches(func): 
    @functools.wraps(func) 
    @mock.patch('foo.settings.USE_FAKE_CONNECTION', False) 
    @mock.patch('foo.settings.DATABASE_URI', 'li://foo') 
    @mock.patch('foo.connection.api.Session.post', autospec=True) 
    def _(*args, **kwargs): 
     return func(*args, **kwargs) 

    return _ 

ahora puede usar eso en su conjunto de pruebas en lugar de una cantidad loca de decoradores por encima de cada función ::

def ChuckNorrisCase(unittest.TestCase): 
    @apply_patches 
    def test_chuck_pwns_none(self): 
     self.assertTrue(None)