Python Tips: Decorators
A very powerful and useful tool in Python

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class.
— Design Patterns
In Python, you can use a function or class to implement decorators to modify the behavior of the function through a wrapper so we don’t have to actually modify the function.
The scenarios for using decorators include loggings, performance testing, transaction processing, caching, permission verification, etc. Decorators are an excellent design to solve such problems.
Our Goal
Through a simple example, to explain why we want to use decorators in our codes. First, we have a function that can print some messages.
Now, there is a new requirement to save the execution log of the function, we can add print('Call foo()')
to the function.
If the other functions have similar requirements, how do we do it? Copy the log code to the other functions? This results in a lot of similar codes. In order to reduce redundant code, we can redefine a new function: specifically process the log, and execute the real business code after the log.
Although the above code meets our requirements, this solution breaks the structure of the code, call the logging code instead of the business code foo()
where it should be called. To handle this problem in a better way, we can use decorators.
Decorator Solution
This is our solution with the decorator in Python.
log_decorator
is a decorator that prints the execution log of the function. To implement the decorator, we use four features of python functions:
def log_decorator(func):
: Functions can be passed around and used as arguments.def log_wrapper(*args, **kwargs)
: Inner functionsreturn log_wrapper
: Returning functions from functions- The function can be assigned to a variable.
@log_decorator
is a syntactic sugar equivalent tofoo = log_decorator(foo)
.
The functools
module is for higher-order functions: functions that act on or return other functions. @functools.wraps(func)
is also a decorator that will preserve information about the original function.
REMEMBER: Add @functools.wraps(func)
before the definition yourwrapper()
.
def log_wrapper(*args, **kwargs)
use *args
and *kwargs
, it will accept an arbitrary number of positional and keyword arguments.
Advanced Decorator
Further, if we need the decorator with some arguments, we need to write a higher-order function that returns a decorator. Please see the code below.
This is a triple nested decorator. It is easier to understand this program if you do not use syntactic sugar.
First, the return value of the log(message)
function is a decorator function log_decorator(func)
, and the return value of the log_decorator(func)
is wrapper function log_wrapper(*args, **kwargs)
.
The program starts with the following line:
foo = log('Call')(foo)
In effect, the program executes log('Call')
, which returns the decorator function log_decorator(func)
, and then calls the returned function with the parameter foo()
function and the return value is eventually the wrapper function.
Python Decorators in Practices
Authentication
Decorators are often used in projects for login verification, permission checking and other scenarios. In Django source code, uses two decorators @login_required
and @permission_required
to check the user’s log-in and permission.
user_passes_test
Source codeThe implementation of user_pass_test
shows that it uses the decorator. In your code, you just need to add @login_required
and @permission_required
before your process to complete the login and permission control.
Logging
In practice, if you suspect that some functions are running too long, increase the system latency. So you want to test the execution time of certain functions onsite, then decorators are a very common way to do this. Decorators are non-intrusive to project code.
The following is a simple demonstration:
The decorator log
print the runtime of a function and return the result of its execution. If you want to calculate the execution time of any function. just add @log
above the function.
Fibonacci sequence
The decorator’s feature of reducing repetitive code can also be used to help us compute the Fibonacci sequence.
We use a decorator to save the result of each fib()
calculation in a dictionary cache
. This solution is more efficient than using recursive ideas.
What’s new in Python 3.9 decorators
Thanks for reading.
Stay Healthy, Stay Coding.
