Decorators in Python

Posted by

What is a decorator in Python?

A decorator is a special function of python that adds some extra functionalities to a normal function without modifying the original code. In other words, decorators act like a wrapper around a normal function.

Why there is a need for a decorator?

Since decorators can add more functionalities to an existing function, developers don’t have to rewrite the whole code. They will only write the code that will do the extra work. Therefore, features of base functions can be enhanced without understanding/some understanding of underlying logic.

What are the different parts of a decorator?

Like normal functions have  header,  body and function call we can expect similar kinds of structures with decorators as well.

Refer to the picture below for function structures.

python decorator partsSimilar to functions, python decorators have a decorator body and decorator call. Please refer to the picture below.

decorator partsDecorator definition: It contains the extra lines of code and the original function in an un-executed state.Therefore,  this is the region where we wrap the original function with extra functionalities.

Decorator call: It  starts with ‘@’ symbol followed by the decorator’s name. Just beneath the decorator call, we have the base function definition.

Base function call: In this part of the code, we call the base function.

The sequence of execution is as follows: base function calldecorator calldecorator definition.

A basic python decorator function:

def decorator_function(base_fx):

    def wrapper_function():

        print("extra codes are executed before base function....")

        return base_fx()

    return wrapper_function

@decorator_function

def base_function():

    print("base function executed ....")

base_function()

The below pic is the screenshot of the above code.

simple python decorator

Explanation:

At line 43, the argument base_fx holds the base function, not base function name rather the base function itself.

Line 44 → 46: The wrapper function is defined. This function will hold the extra logic that would be executed before the base function.

Line 47:  The wrapper function is returned along with the “unexecuted” base function.

Line 49: Decorator_function is called along with base function definition.

Line 53: Base_function is called, that executes the decorator function and the base function is executed just after the execution of wrapper function.

How python decorators are un-wrapped?

The code below shows the base function call, the decorator call, and the base function definition.

@decorator_function

def base_function():

    print("base function executed ....")

base_function()

We can also rewrite the code in a more expanded form as shown below.

def base_function():

    print("base function executed ....")

display = decorator_function(base_function)

display()

Since python treats functions as first-class objects, hence we can pass functions as parameters of another function and we can also assign functions to another variable. Therefore, the base_function is passed as an argument of decorator_function.

How to pass arguments to the base function when it is decorated?

In the above examples, we saw the working mechanism of a python decorator. There can be situations where the base function takes arguments. In such cases, we pass the arguments in the wrapper function and return the base function with the same arguments. Refer to the picture below:

arguments pass python decorator Python Decorator with arguments:

The decorators can also take arguments. The arguments can be different in different decorator calls. Therefore, in each decorator call, we can add some more user-defined functionality to the base function.

In the code below, @prefix_decorator(“Hi…”) is a decorator call and it takes the string “Hi…” as an argument. Similarly, it can take any text as an argument, which will be displayed before the execution of the display function. The code below demonstrates the same.

def prefix_decorator(text):

    def decorator_function(orginal_fx):

        def wrapper_function(x,y):

            print(text)

            result = orginal_fx(x,y)

            return result

        return wrapper_function

    return decorator_function    

@prefix_decorator("Hi...")

def display(x,y):

    print("value of x and y {0} and {1}".format(x,y))

display(5,6)

The pic below shows the data flow:

python decorator with arguments

Explanation:

Line 16: The display function is called and it takes 5 and 5 as its arguments.

Line 13: The display function is defined.

Line 12: Display function is decorated by prefix_decorator function with “Hi… ” as an argument.

Line 3: Prefix_decorator is defined with “text” as arguments. Here, parameter “text “will receive the content that is transmitted from prefix_decorator call at line number 12.

Line 4: Decorator_function is defined with “original_fx” as an argument. Here, the display function is passed as a whole not the function name only.

Line 5: Wrapper_function is defined with x and y as arguments. The arguments x and y are the arguments of the display function.

Line 6: The print statement is executed before the execution of the original_function, which demonstrates the core feature of the decorator.

Line 7: The original function is executed which is nothing but the display function with x and y as function parameters. The return value of the display function is stored in the result variable.

Line 8: The wrapper function is returned waiting to be executed.

Line 9: Similarly, the decorator function is returned waiting to be executed.

The Flask and Django applications heavily uses decorators with arguments. In these applications, decorators’ arguments specify routes to different web pages. Refer to the code below.

@app.route("/hello")

def hello_world():

   return "hello world"

Conclusion:

Python Decorator are special functions using which we can add extra functionality to an existing function. In this blog we learned to create our own decorators, we also learned to unwrap a decorator.  Decorators with arguments are very useful as it provides us the freedom to reuse the decorator multiple times.

 

Leave a Reply

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