es más bien subjetiva decir si hay "ventajas" para cada método.
Sin embargo, una buena comprensión de lo que sucede bajo el capó lo haría natural para elegir la mejor opción para cada ocasión.
Un decorador (hablando de decoradores de función), es simplemente un objeto invocable que toma una función como su parámetro de entrada. Python tiene su diseño bastante interesante que permite a crear otros tipos de objetos invocables, además de funciones, y se puede usar para crear código más fácil de mantener o más corto en ocasiones.
decoradores se añadieron de nuevo en Python 2.3 como un "atajo sintáctico" para
def a(x):
...
a = my_decorator(a)
Además de eso, que se suele llamar decoradores algunos "callables" que prefiere ser "fábricas decorador" - cuando utilizamos este tipo :
@my_decorator(param1, param2)
def my_func(...):
...
la llamada se realiza a "my_decorator" con param1 y param2 - a continuación, devuelve un objeto que será llamado de nuevo, esta vez teniendo "mi_func" como un parámetro. Entonces, en este caso, técnicamente el "decorador" es lo que devuelve el "my_decorator", lo que lo convierte en una "fábrica decoradora" .
Ahora, cualquiera de los decoradores o "fábricas decoradoras" como se describe normalmente tienen que mantener algún estado interno. En el primer caso, lo único que conserva es una referencia a la función original (la variable llamada f
en sus ejemplos). Una "fábrica decoradora" puede querer registrar variables de estado adicionales ("param1" y "param2" en el ejemplo anterior).
Este estado adicional, en el caso de decoradores escritos como funciones, se mantiene en variables dentro de las funciones adjuntas, y se accede como variables "no locales" por la función de envoltura real. Si se escribe una clase adecuada, se pueden mantener como variables de instancia en la función de decorador (que se verá como un "objeto invocable", no como una "función"), y el acceso a ellos es más explícito y más legible.
Por lo tanto, para la mayoría de los casos es cuestión de legibilidad si prefiere un enfoque u otro: para decoradores cortos y simples, el enfoque funcional es a menudo más legible que uno escrito como clase - mientras que a veces una elaborada, especialmente una "fábrica decoradora" aprovechará al máximo el consejo "plano es mejor que anidado" para la codificación Python.
Considere:
def my_dec_factory(param1, param2):
...
...
def real_decorator(func):
...
def wraper_func(*args, **kwargs):
...
#use param1
result = func(*args, **kwargs)
#use param2
return result
return wraper_func
return real_decorator
contra de esta solución "híbrida":
class MyDecorator(object):
"""Decorator example mixing class and function definitions."""
def __init__(self, func, param1, param2):
self.func = func
self.param1, self.param2 = param1, param2
def __call__(self, *args, **kwargs):
...
#use self.param1
result = self.func(*args, **kwargs)
#use self.param2
return result
def my_dec_factory(param1, param2):
def decorator(func):
return MyDecorator(func, param1, param2)
return decorator
actualización: Missing formas "pura clase" de decoradores
Ahora, tenga en cuenta el "híbrido" método toma lo mejor de ambos mundos tratando de mantener el código más corto y más fácil de leer. Una "fábrica decorador" completa definida exclusivamente con las clases sería o bien tendrá dos clases, o un atributo "modo" para saber si ha sido llamado a registrar la función decorado o llamar realmente la función final:
class MyDecorator(object):
"""Decorator example defined entirely as class."""
def __init__(self, p1, p2):
self.p1 = p1
...
self.mode = "decorating"
def __call__(self, *args, **kw):
if self.mode == "decorating":
self.func = args[0]
self.mode = "calling"
return self
# code to run prior to function call
result = self.func(*args, **kw)
# code to run after function call
return result
@MyDecorator(p1, ...)
def myfunc():
...
Y, por último un puro, decorador de "colar blanco" se define con dos clases - tal vez mantener las cosas más separados, pero el aumento de la redundancia a un punto que no se puede decir que es más fácil de mantener:
class Stage2Decorator(object):
def __init__(self, func, p1, p2, ...):
self.func = func
self.p1 = p1
...
def __call__(self, *args, **kw):
# code to run prior to function call
...
result = self.func(*args, **kw)
# code to run after function call
...
return result
class Stage1Decorator(object):
"""Decorator example defined as two classes.
No "hacks" on the object model, most bureacratic.
"""
def __init__(self, p1, p2):
self.p1 = p1
...
self.mode = "decorating"
def __call__(self, func):
return Stage2Decorator(func, self.p1, self.p2, ...)
@Stage1Decorator(p1, p2, ...)
def myfunc():
...
Una cosa importante: sus funciones de contenedor reales llaman la 'función F' original, pero no devuelven su valor devuelto al destinatario de la llamada: esto muy probablemente conduciría a un comportamiento incorrecto. – jsbueno
posible duplicado de [Diferencia entre clases de decorador y decorador] (http://stackoverflow.com/questions/4650333/difference-between-decorator-classes-and-decorator-functions) –