Decorators¶
Functions can also be passed as arguments to other functions and return the results of other functions. For example, it is possible to write a Python function that takes another function as a parameter, embeds it in another function that does something similar, and then returns the new function. This new combination can then be used instead of the original function:
1 >>> def inf(func):
2 ... print("Information about", func.__name__)
3 ... def details(*args):
4 ... print("Execute function", func.__name__, "with the argument(s)")
5 ... return func(*args)
6 ... return details
7 ...
8 >>> def my_func(*params):
9 ... print(params)
10 ...
11 >>> my_func = inf(my_func)
12 Information about my_func
13 >>> my_func("Hello", "Pythonistas!")
14 Execute function my_func with the argument(s)
15 ('Hello', 'Pythonistas!')
- Line 2
The
inf
function outputs the name of the function it wraps.- Line 6
When finished, the
inf
function returns the wrapped function.
A decorator is syntactic sugar for this process and allows you to wrap one function inside another with a one-line addition. You still get exactly the same effect as with the previous code, but the resulting code is much cleaner and easier to read. Using a decorator simply consists of two parts:
the definition of the function to wrap or decorate other functions, and
the use of an
@
followed by the decorator just before the wrapped function is defined.
The decorator function should take a function as a parameter and return a function, as follows:
1 >>> @inf
2 ... def my_func(*params):
3 ... print(params)
4 ...
5 Information about my_func
6 >>> my_func("Hello", "Pythonistas!")
7 Execute function my_func with the argument(s)
8 ('Hello', 'Pythonistas!')
- Line 1
The function
my_func
is decorated with@inf
.- Line 7
The wrapped function is called after the decorator function is finished.
functools
¶
The Python functools
module is intended for higher-order functions, for
example functions that act on or return other functions. Mostly you can use them
as decorators, such as:
functools.cache()
Simple, lightweight, function cache as of Python ≥ 3.9, sometimes called memoize. It returns the same as
functools.lru_cache()
with the parametermaxsize=None
, additionally creating a Dictionaries with the function arguments. Since old values never need to be deleted, this function is then also smaller and faster. Example:1>>> from functools import cache 2>>> @cache 3... def factorial(n): 4... return n * factorial(n - 1) if n else 1 5... 6>>> factorial(8) 740320 8>>> factorial(10) 93628800
- Line 6
Since there is no previously stored result, nine recursive calls are made.
- Line 8
makes only two new calls, as the other results come from the cache.
functools.wraps()
This decorator makes the wrapper function look like the original function with its name and properties.
>>> from functools import wraps >>> def my_decorator(f): ... @wraps(f) ... def wrapper(*args, **kwargs): ... """Wrapper docstring""" ... print("Call decorated function") ... return f(*args, **kwargs) ... return wrapper ... >>> @my_decorator ... def example(): ... """Example docstring""" ... print("Call example function") ... >>> example.__name__ 'example' >>> example.__doc__ 'Example docstring'
Without
@wraps
decorator, the name and docstring of the wrapper method would have been returned instead:>>> example.__name__ 'wrapper' >>> example.__doc__ 'Wrapper docstring'