Skip to content

Python function decorator

python function decorator post logo

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:

  1. A decorator is a function;
  2. A decorator takes the decorated function as an argument;
  3. A decorator returns a new function;
  4. 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!'

References

Leave a Reply

Your email address will not be published. Required fields are marked *