How do I pass extra arguments to a Python decorator? How do I pass extra arguments to a Python decorator? python python

How do I pass extra arguments to a Python decorator?


Since you are calling the decorator like a function, it needs to return another function which is the actual decorator:

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

The outer function will be given any arguments you pass explicitly, and should return the inner function. The inner function will be passed the function to decorate, and return the modified function.

Usually you want the decorator to change the function behavior by wrapping it in a wrapper function. Here's an example that optionally adds logging when the function is called:

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

The functools.wraps call copies things like the name and docstring to the wrapper function, to make it more similar to the original function.

Example usage:

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


Just to provide a different viewpoint: the syntax

@exprdef func(...): #stuff

is equivalent to

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

In particular, expr can be anything you like, as long as it evaluates to a callable. In particular particular, expr can be a decorator factory: you give it some parameters and it gives you a decorator. So maybe a better way to understand your situation is as

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

which can then be shortened to

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

Of course, since it looks like decorator_factory is a decorator, people tend to name it to reflect that. Which can be confusing when you try to follow the levels of indirection.


Just want to add some usefull trick that will allow to make decorator arguments optional. It will also alows to reuse decorator and decrease nesting

import functoolsdef 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@myDecoratordef pow3(i):    return i**3@myDecorator_2def pow4(i):    return i**4print pow2(2)print pow3(2)print pow4(2)