Python

Python function decorator

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

bgasparotto

Recent Posts

Got permission denied while trying to connect to the Docker daemon socket

This guide will show you how to fix the error Got permission denied while trying…

2 years ago

Python virtual environment on Intellij IDEA

This guide will show you how to create a Python virtual environment on Intellij IDEA…

2 years ago

Find and kill processes on Linux and Mac by port number

This tutorial will quickly show you how to to find and kill processes on Linux,…

2 years ago

Python: Relocation R_X86_64_PC32 against symbol can not be used when making a shared object Error

This guide shows a possible solution for Python error Relocation R_X86_64_PC32 against symbol can not…

2 years ago

Kubernetes useful commands

I condensed below a cheat sheet of Kubernetes useful commands. I will keep updating this…

2 years ago

Create aliases for commands on Unix

This tutorial will show how to create aliases for commands on Unix based systems, so…

4 years ago