A decorator is the name used for a software design pattern. Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated.

Decorators allow you to make simple modifications to callable objects like functions, methods, or classes.

The first assumption is

  • “A decorator is a function that alter another function.”

Let’s go ahead

  • A decorator receive as an input the function that will be decorated.
  • The decorator after altered the function, will return the decorated function.

the following few lines show a simple decorator:

def fun_decorator(fun_to_be_decorated):
    fun_to_be_decorated.info = "decorated"
    return fun_to_be_decorated

def myfunc():
    print("myfunc")
    print(myfunc.info)

On the shell

>>> myfunc


>>> myfunc()
myfunc
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 7, in myfunc
    print(myfunc.info)
AttributeError: 'function' object has no attribute 'info'

>>> 

Now we are going to apply the decorator

>>> decorated = fun_decorator(myfunc)

>>> decorated


>>> myfunc

And the output produced is a just bit different:

>>> decorated()
myfunc
decorated

>>> 

>>> myfunc()
myfunc
decorated

>>>

Going ahead….
Simple decorator with a wrapper function
A wrapper if a function defined inside the decorator function that “wraps” the function to be decorated. In such a way it provides the capability to execute code before and after the function to be decorated.

from functools import wraps

def fun_wrapper_decorator(fun_to_be_decorated):
    @wraps(fun_to_be_decorated)
    def wrapper(*args, **kwargs):
        print("before")
        fun_to_be_decorated()
        print("after")
    return wrapper

@fun_wrapper_decorator
def myfunc1():
    print("myfunc")

As you can see from the code above, the “@” keyword can be used to apply the decorator to a function.

let’s give a try using the shell:

>>> myfunc1()
before
myfunc
after

more decorators can be used in cascade, and they will be run in cascade, for example let’s add a second decorator to the code. The new code will be as follows:

from functools import wraps

def fun_wrapper_decorator1(fun_to_be_decorated):
    @wraps(fun_to_be_decorated)
    def wrapper(*args, **kwargs):
        print("before1")
        fun_to_be_decorated()
        print("after1")
    return wrapper

def fun_wrapper_decorator(fun_to_be_decorated):
    @wraps(fun_to_be_decorated)
    def wrapper(*args, **kwargs):
        print("before")
        fun_to_be_decorated()
        print("after")
    return wrapper

@fun_wrapper_decorator1
@fun_wrapper_decorator
def myfunc1():
    print("myfunc")

and from the shell:

>>> myfunc1()
before1
before
myfunc
after
after1

Simple and powerful, next time we will see some real code.

Take a look at “Python decorators in the real world” for more about python decorators.

Gg1