2012-05-16 14 views
9

¿La biblioteca estándar de Python tiene un atajo para escribir decoradores que aceptan argumentos?Python: atajo para escribir decoradores que aceptan argumentos?

Por ejemplo, si quiero escribir un decorador como with_timeout(timeout):

@with_timeout(10.0) 
def cook_eggs(eggs): 
    while not eggs.are_done(): 
     eggs.cook() 

tengo que escribir algo como:

def with_timeout(timeout): 
    _func = [None] 
    def with_timeout_helper(*args, **kwargs): 
     with Timeout(timeout): 
      return _func[0](*args, **kwargs) 
    def with_timeout_return(f): 
     return functools.wraps(f)(with_timeout_helper) 
    return with_timeout_return 

Pero eso es muy detallado. ¿Hay un atajo que haga que los decoradores que aceptan argumentos sean más fáciles de escribir?

Nota: Me doy cuenta de que también es posible utilizar tres funciones anidadas para implementar decoradores con argumentos ... Pero eso también se siente un poco subóptimo.

Por ejemplo, posiblemente, algo así como una función @decorator_with_arguments:

@decorator_with_arguments 
def timeout(f, timeout): 
    @functools.wraps(f) 
    def timeout_helper(*args, **kwargs): 
     with Timeout(timeout): 
      return f(*args, **kwargs) 
    return timeout_helper 
+0

Si necesita más ayuda con decoradores y anotaciones, vea la publicación de mi blog aquí. http://blog.mattalcock.com/2013/1/5/decorates-and-annotations/ –

Respuesta

7

tiendo a escribir mis decoradores como clases para ser honesto

class TestWithArgs(object): 
    def __init__(self, *deco_args, **deco_kwargs): 
     self.deco_args = deco_args 
     self.deco_kwargs = deco_kwargs 
    def __call__(self, func): 
     def _wrap(self, *args, **kwargs): 
      print "Blah blah blah" 
      return func(*args, **kwargs) 
     return _wrap 

Su nada si no un poco más clara

4

I Sé que dijiste que no se siente bien, pero todavía siento que usar tres modelos anidados es la solución más limpia. Las dos funciones internas son solo la forma "normal" de definir un decorador para una función que toma argumentos (vea example en los documentos de python para @wraps). El exterior es realmente solo una función que toma una discusión y devuelve un decorador.

def with_timeout(timeout): 
    def decorator(f): 
     @wraps(f) 
     def wrapper(*args, **kwargs): 
      with Timeout(timeout): 
       return f(*args, **kwargs) 
     return wrapper 
    return decorator 
+0

Lo siento, pero no estoy de acuerdo. No hay nada totalmente limpio sobre tres funciones anidadas (o, para el caso, la forma en que lo hago). Es un truco. Mejor sería, por ejemplo, un decorador '@ decorator_with_arguments' (ver mi pregunta actualizada). –

4

Sobre la base de la sugerencia de Jakob, he implementado una pequeña clase Decorator, que siento hace un trabajo bastante decente:

class Decorator(object): 
    def __call__(self, f): 
     self.f = f 
     return functools.wraps(f)(lambda *a, **kw: self.wrap(*a, **kw)) 

    def wrap(self, *args, **kwrags): 
     raise NotImplemented("Subclasses of Decorator must implement 'wrap'") 

class with_timeout(Decorator): 
    def __init__(self, timeout): 
     self.timeout = timeout 

    def wrap(self, *args, **kwargs): 
     with Timeout(timeout): 
      return self.f(*args, **kwargs) 
+1

He corregido un error tipográfico en el método 'with_timeout'' warp' –

+0

D'oh! Gracias. Esa es probablemente la octava vez que escribo 'warp' en lugar de 'wrap' esta noche. Y no he escrito 'wrap' muchas veces. –

0

En primer lugar, podemos definir un poco de meta-decorador:

def decorator_with_arguments(wrapper): 
    return lambda *args, **kwargs: lambda func: wrapper(func, *args, **kwargs) 

eso nos permite crear decoradores que aceptan argumentos de este modo:

@decorator_with_arguments 
def my_wrapper(func, *decorator_args, **decorator_kwargs): 
    def wrapped(*call_args, **call_kwargs): 
     print "from decorator:", decorator_args, decorator_kwargs 
     func(*call_args, **call_kwargs) 
    return wrapped 

que luego pueden ser utilizados normalmente:

@my_wrapper(1, 2, 3) 
def test(*args, **kwargs): 
    print "passed directly:", args, kwargs 

test(4, 5, 6) 

Adición de functools.wraps decoración se deja como un ejercicio :)

0

Otra a tener, sin el uso de lambdas:

def decorator_with_arguments(f): 
    @functools.wraps(f) 
    def with_arguments_helper(*args, **kwargs): 
     def decorator(g): 
      return f(g, *args, **kwargs) 
     return decorator 
    return with_arguments_helper 
Cuestiones relacionadas