Decorators in Python 3

A Python decorator is a specific change to the Python syntax that allows us to conveniently alter functions and methods. In simpler words, a decorator takes in a function, adds some functionality and returns it.

Example:

def my_decorator(func):
    def inner():
        print("Decoration before function call")
        func()
        print("Decoration after function call")

    return inner

@my_decorator
def simple_print():
    print("Hello from simple_print")

simple_print()
print()

#output
Decoration before function call
Hello from simple_print
Decoration after function call

The function my_decorator(func) is the decorator function. To use a decorator, place @decorator_function_name before the function definition that you want to decorate.

Doing the following

@my_decorator
def simple_print():
    print("Hello from simple_print")

is equivalent to doing

def simple_print():
    print("Hello from simple_print")
simple_print = my_decorator(simple_print)

Decorators can also work with functions that accept arguments.

Example:

def check_valid_division(func):
    def inner(a,b):
        if b == 0:
            print("Division by zero is not allowed")
        else:
            func(a,b)
    
    return inner

@check_valid_division
def divide(a, b):
    print(a/b)

divide(10,5)
divide(10,0)

#output
2.0
Division by zero is not allowed

Notice how we decorated the divide(a,b) function to prevent throwing a ZeroDivisionError which would have been raised if it was not decorated.

It is also possible to chain multiple decorators together.

Example:

def print_star(func):
    def inner(val):
        print("*" * 20)
        func(val)
        print("*" * 20)
    
    return inner

def print_percent(func):
    def inner(val):
        print("%" * 20)
        func(val)
        print("%" * 20)
    
    return inner

@print_star
@print_percent
def fancy_print(val):
    print(val)

fancy_print("Hello World")
print()

@print_percent
@print_star
def fancy_print_different(val):
    print(val)

fancy_print_different("Different decoration")

#output
********************
%%%%%%%%%%%%%%%%%%%%
Hello World
%%%%%%%%%%%%%%%%%%%%
********************

%%%%%%%%%%%%%%%%%%%%
********************
Different decoration
********************
%%%%%%%%%%%%%%%%%%%%

The syntax

@print_star
@print_percent
def fancy_print(val):
    print(val)

is equivalent to

def fancy_print(val):
    print(val)
fancy_print = print_star(print_percent(fancy_print))

Code for today’s plog is here.

References: