
This guide will show you how to create a Python function decorator with a few easy steps, to help you separate different concerns in code. This guide does not aim to provide reference material regarding decorators, but rather explain the basics and provide a starting point for implementing your own decorator.
What is a decorator?
A Python decorator is similar to a Java annotation if you are familiar with the JVM language, where you decorate or “annotate” a function prefixing the decorator’s name with @. For example, the decorator below publishes the function hello() on the url / on a Flask application:
@app.route('/')
def hello() -> str:
return 'Hello world from Flask!'
More broadly speaking, a decorator is a structural design pattern that lets you add behaviour to an object, by wrapping its decorated function in a new function that has the same signature.
Creating a Python function decorator
In Python, a function decorator should follow these rules:
- A decorator is a function;
- A decorator takes the decorated function as an argument;
- A decorator returns a new function;
- A decorator maintains the decorated function’s signature.
Therefore, we can use the Python decorator template below as a starting point:
from functools import wraps
def decorator_name(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 1. Code to execute BEFORE calling the decorated function
# 2. Call the decorated function as required, returning if needed
# return func(*args, **kwargs)
# 3. Code to execute INSTEAD of calling the decorated function.
return wrapper
Let’s now turn this decorator into something more useful. The decorator below checks whether a session contains the the key logged_in, if it does, the decorated function executes, otherwise, it returns a message saying the caller is not logged in:
from functools import wraps
from flask import session
def authenticated(func):
@wraps(func)
def wrapper(*args, **kwargs):
if 'logged_in' in session:
return func(*args, **kwargs) # executes the decorated function
return 'Not logged in'
return wrapper
Now, we can apply this new decorator to the hello() function we saw at the beginning:
@app.route('/')
@authenticated
def hello() -> str:
return 'Hello world from Flask!'