Decoradores de la statement

Tenemos un código que se ve así:

from third_party_library import foo for n in range(3): try: foo(args) break except: print "Retry %i / 3" % n 

Me gustaría usar un decorador, permitiendo que nuestro código sea más sencillo, con este aspecto:

 from third_party_library import foo @retry(3) foo(args) 

Esto da un error de syntax. ¿Me estoy perdiendo algo o Python simplemente no permite a los decoradores hacer declaraciones?

Los decoradores solo se pueden aplicar a definiciones de funciones y clases tales como:

 @decorator def func(): ... @decorator class MyClass(object): ... 

No puedes usarlos en ningún otro lugar en el idioma.


Para hacer lo que quieras, puedes hacer una función de retry normal y pasar foo y args como argumentos. La implementación se vería algo así:

 def retry(times, func, *args, **kwargs): for n in xrange(times): try: func(*args, **kwargs) break except Exception: # Try to catch something more specific print "Retry %i / %i" % (n, times) 

Python no permite decoradores en las declaraciones; solo se permiten en las definiciones de clase y función. Puedes ver esto cerca de la parte superior de la especificación gtwigtical .

Los decoradores fueron introducidos en Python 2.4. Inicialmente, solo se admitían para declaraciones de funciones y métodos ( PEP 318 ).

Python 3 los extendió a las declaraciones de clase ( PEP 3129 ).

Los decoradores no son compatibles con otras construcciones de lenguaje.

Es cierto que los decoradores no se pueden aplicar a las declaraciones, y puede hacer la función de rebash como se describe en la otra respuesta. En mi opinión, sin embargo, hay un enfoque más Pythonesque, es decir, es el uso del administrador de contexto. Permítame reformular su problema de una manera ejecutable, y deje que el código hable:

 import random def foo(): """The function you were importing, now runnable""" n = random.randint(1,10) print "Got", n, if n < 8: raise Exception("Failed") for n in range(3): try: foo() break except: print "Retry %i / 3" % n 

Usando un ContextManager (generado automáticamente), el código se verá así:

 import random def foo(): """The function you were importing, now runnable""" n = random.randint(1,10) print "Got", n, if n < 8: raise Exception("Failed") from contextlib import contextmanager @contextmanager def retry(): """Something similar to a decorator for statements""" try: yield except: print "Retry %i / 3" % n for n in range(3): with retry(): foo() break 

Como puede ver, la solución no es perfecta, ya que ContextManager puede ejecutar la (s) statement (es) exactamente una vez (activada en el momento del rendimiento) y no en un bucle. Sin embargo, al menos para este simple ejemplo, me parece más claro, aunque el bucle se debe hacer afuera. Consulte https://docs.python.org/2.7/library/contextlib.html y https://docs.python.org/2/reference/datamodel.html#context-managers para obtener detalles