¿Cómo paso argumentos adicionales a un decorador de Python?

Tengo un decorador como el de abajo.

def myDecorator(test_func): return callSomeWrapper(test_func) def callSomeWrapper(test_func): return test_func @myDecorator def someFunc(): print 'hello' 

Quiero mejorar este decorador para aceptar otro argumento como el siguiente

 def myDecorator(test_func,logIt): if logIt: print "Calling Function: " + test_func.__name__ return callSomeWrapper(test_func) @myDecorator(False) def someFunc(): print 'Hello' 

Pero este código da el error,

TypeError: myDecorator () toma exactamente 2 argumentos (1 dado)

¿Por qué la función no se pasa automáticamente? ¿Cómo paso la función explícitamente a la función decoradora?

Ya que está llamando al decorador como una función, necesita devolver otra función que sea el decorador real:

 def my_decorator(param): def actual_decorator(func): print("Decorating function {}, with parameter {}".format(func.__name__, param)) return function_wrapper(func) # assume we defined a wrapper somewhere return actual_decorator 

A la función externa se le darán todos los argumentos que pase explícitamente, y debe devolver la función interna. A la función interna se le pasará la función para decorar y devolver la función modificada.

Por lo general, usted desea que el decorador cambie el comportamiento de la función envolviéndolo en una función de envoltura. Aquí hay un ejemplo que opcionalmente agrega el registro cuando se llama a la función:

 def log_decorator(log_enabled): def actual_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): if log_enabled: print("Calling Function: " + func.__name__) return func(*args, **kwargs) return wrapper return actual_decorator 

La llamada functools.wraps copia elementos como el nombre y la cadena de documentos a la función de envoltura, para que sea más similar a la función original.

Ejemplo de uso:

 >>> @log_decorator(True) ... def f(x): ... return x+1 ... >>> f(4) Calling Function: f 5 

Solo para proporcionar un punto de vista diferente: la syntax.

 @expr def func(...): #stuff 

es equivalente a

 def func(...): #stuff func = expr(func) 

En particular, expr puede ser lo que quieras, siempre y cuando se evalúe como llamable. En particular , expr puede ser una fábrica de decoradores: le das algunos parámetros y te da un decorador. Así que tal vez una mejor manera de entender su situación es como

 dec = decorator_factory(*args) @dec def func(...): 

que luego se puede acortar a

 @decorator_factory(*args) def func(...): 

Por supuesto, dado que parece que decorator_factory es un decorador, las personas tienden a nombrarlo para reflejar eso. Lo que puede ser confuso cuando intentas seguir los niveles de direccionamiento indirecto.

Solo desea agregar algún truco útil que permita hacer opcionales los argumentos del decorador. También permite reutilizar decorador y disminuir la anidación.

 import functools def myDecorator(test_func=None,logIt=None): if not test_func: return functools.partial(myDecorator, logIt=logIt) @functools.wraps(test_func) def f(*args, **kwargs): if logIt==1: print 'Logging level 1 for {}'.format(test_func.__name__) if logIt==2: print 'Logging level 2 for {}'.format(test_func.__name__) return test_func(*args, **kwargs) return f #new decorator myDecorator_2 = myDecorator(logIt=2) @myDecorator(logIt=2) def pow2(i): return i**2 @myDecorator def pow3(i): return i**3 @myDecorator_2 def pow4(i): return i**4 print pow2(2) print pow3(2) print pow4(2) 

Solo otra forma de hacer decoradores. De esta manera me parece la forma más fácil de envolver mi cabeza.

 class NiceDecorator: def __init__(self, param_foo='a', param_bar='b'): self.param_foo = param_foo self.param_bar = param_bar def __call__(self, func): def my_logic(*args, **kwargs): # whatever logic your decorator is supposed to implement goes in here print('pre action baz') print(self.param_bar) # including the call to the decorated function (if you want to do that) result = func(*args, **kwargs) print('post action beep') return result return my_logic # usage example from here on @NiceDecorator(param_bar='baaar') def example(): print('example yay') example()