DEV Community

Cover image for Multiple Decorators in Python
AYON KARMAKAR
AYON KARMAKAR

Posted on

Multiple Decorators in Python

Before going to Multiple Decorators let’s first understand what is Decorators

Decorators in Python

Decorators are used to change the functionality of a function without modifying the actual function by taking the function as an argument.

code structure for Decorators in python

@Dec_1
def A_funk():
    # functionality
    return If_Needed_to_return
A_funk()
Enter fullscreen mode Exit fullscreen mode

Code explanation :-

def Add_Style(funk):                  # step 2  funk -> DisplayName
    def inner():                      # step 4
        print("<-",end="")            #step 5
        funk()                        #step 6 call to funk() -> displayName() function
        print("->",end="")            #step 9
    return inner                      # step 3 inner function will call


def DisplayName():           # step 7
    print("TutorialsPoint",end="")   # step 8

gg = Add_Style(DisplayName)    # step 1: Add_Style function will take DisplayName function as an argument
gg() 

# -or- 
# Add_Style(DisplayName)()

Enter fullscreen mode Exit fullscreen mode

Let’s take an example where we use decorators:

The DisplayNamefunction will pass as an argument to the add_Style function and the text changest "TutorialsPoint" to "<-TutorialsPoint->"

def Add_Style(funk):
    def inner():
        print("<-",end="")
        funk()
        print("->",end="")
    return inner

@Add_Style
def DisplayName():
    print("TutorialsPoint",end="")

DisplayName()
Enter fullscreen mode Exit fullscreen mode

Output :-

<-TutorialsPoint->
Enter fullscreen mode Exit fullscreen mode

Where to use decorators (with an example):

If decorators are not used :-

import time
def funk1():
    start_time = time.time()

    # A big Task which may be any code that takes some time
    print("funk1() Task is Done")

    end_time = time.time()
    print(f"The time taken by this function is {(end_time - start_time) * 1000} Milli Second")

def funk2():
    start_time = time.time()

    # A big Task which may be any code that takes some time
    print("funk2() Task is Done")

    end_time = time.time()
    print(f"The time taken by this function is {(end_time - start_time) * 1000} Milli Second")

def funk3():
    start_time = time.time()

    # A big Task which may be any code that takes some time
    print("funk3() Task is Done")

    end_time = time.time()
    print(f"The time taken by this function is {(end_time - start_time) * 1000} Milli Second")
funk1()
funk2()
funk3()
Enter fullscreen mode Exit fullscreen mode

Output :-

<-TutorialsPoint->
Enter fullscreen mode Exit fullscreen mode

In the above code, you can see that we are repeating the code in every function to calculate the time taken by a function.

To reduce the repetitive task we use Decorators.

Now using Decorators in python

import time

def time_taken(funk):
    def inner():
        start_time = time.time()
        funk()
        end_time = time.time()
        print(f"The time taken by this function is {(end_time - start_time) * 1000} Milli Second")
    return inner

@time_taken
def funk1():
    # A big Task which may be any code that takes some time
    print("funk1() Task is Done")

@time_taken
def funk2():
    # A big Task which may be any code that takes some time
    print("funk2() Task is Done")

@time_taken
def funk3():
    # A big Task which may be any code that takes some time
    print("funk3() Task is Done")

funk1()
funk2()
funk3()

Enter fullscreen mode Exit fullscreen mode

Now, No need to repeat the code in every function to calculate the time taken by a function.

Multiple Decorators in Python

In simple terms, If a function has multiple decorators we call it as chain of function decorators.

code structure for Multiple Decorators in python

@Dec_1
@Dec_2
@Dec_3
@Dec_4
.
.
.

def A_funk():
    # functionality
    return If_Needed_to_return
A_funk()
Enter fullscreen mode Exit fullscreen mode

Let's understand Chaining of Decorators with an example:

With two decorators

@add_Borders

@add_Style

def add_Borders(funk):
    # This function will take add_Style function as an argument
    # add_Style -> funk
    def inner():
        print("|",end="")
        funk()
        print("|",end="")
    return inner
def add_Style(funk):
    # This function will take DisplayName function as an argument
    # DisplayName -> funk

    def inner():
        print("<-",end="")
        funk()
        print("->",end="")
    return inner
@add_Borders
@add_Style
def DisplayName():
    # function which will display "TutorialsPoint"
    print("TutorialsPoint",end="")

# GG = add_Borders(add_Style(DisplayName))
# GG()

DisplayName()
Enter fullscreen mode Exit fullscreen mode

Output: -

|<-TutorialsPoint->|
Enter fullscreen mode Exit fullscreen mode

We can also use:

GG = add_Borders(add_Style(DisplayName))
GG()
Enter fullscreen mode Exit fullscreen mode

Another example in which we are returning the strings :

def add_Borders(funk):
    def inner():
        return "|" + funk() + "|"
    return inner
def add_Style(funk):

    def inner():
        return "<-" + funk() + "->"
    return inner
@add_Borders
@add_Style
def DisplayName():
    return "TutorialsPoint"

print(DisplayName())
Enter fullscreen mode Exit fullscreen mode

Explanation :


The Decorator execution order is from bottom to top for example in our case @add_Style to @add_Borders

let's the base function be DisplayName()


step 1:
First the base function will pass as an argument to the add_Style function and the text changest "TutorialsPoint" to "<-TutorialsPoint->"

step 2:
Then the add_Style function will pass as an argument to add_Borders function and the text changest "<-TutorialsPoint->" to "|<-TutorialsPoint->|"

step3:
it will print "|<-TutorialsPoint->|"

In this above code we can understand that how Multiple Decorators are helping to change the functionality of a DisplayName() function twice first it's adding add_style "<- plaintext ->" and then adding add_Borders " | plaintext | " & if needed we can add more additional functionality to DisplayName() function.

There is a builtin standard library called inspect module in Python where we can use getsource() method to display the source code of any python object

Top comments (0)