Udit Vashisht
Author: Udit Vashisht


Python Decorators - What are Python Decorators - A Complete Tutorial

  • 7 minutes read
  • 42 Views
Python Decorators - What are Python Decorators - A Complete Tutorial
var aax_size='728x90'; var aax_pubname = 'saralgyaan0d-21'; var aax_src='302';

    Table of Contents

What are Python Decorators ?

Python Decorators function is just a function that takes another function as an argument and add extra features or behaviour to another function without altering the source code of original function.

The python decorators take advantage of the fact that the functions in python are first class functions it means that we can pass a function as an argument to other function. We can return functions and assign functions to the variables.

What is the use of Python Decorators?

If we want to change the functionality or add a feature to an existing function to which we do not have access or we do not want to change, then we use decorators. Also, if there is certain kind of functionality like timing a function or logging a function, and we want to add this functionality to multiple functions in that case we can create a decorator function and then apply that decorator function to all those functions.

How to create a simple Python Decorators Function?

As discussed above, Python decorators function is nothing but a function which takes another function as an argument. So we can create it using the following syntax :-

def decorator_function(func):
    def wrapper_function():
        print (f'Wrapper function ran before {func.__name__}.')
        return func()
    return wrapper_function

How to apply Python Decorators to a function?

To apply the python decorators to a function first we will create a simple function which will just have a print statement:-

def a_function():
    print ('A function ran.')

Now, to decorate it we can do the following :-

decorated_function = decorator_function(a_function)
decorated_function()

# Output

Wrapper function ran before a_function.
A function ran.

If you have a look at the above output, you will see that the print statement from the wrapper function of the python decorator ran first. But this is a longer way to apply a decorator to a function. The general syntax to use is to add @decorator_function at the very top of the function.

@decorator_function
def a_function():
    print ('A function ran.')

a_function()

# Output
Wrapper function ran before a_function.
A function ran.

How to create Python Decorators with arguments?

In the above example our function didn’t take any argument but if we have a following function which takes certain arguments :-

def b_function(name, age):
    print(f'{name} is {age} years old.')

Now, if we try to decorate it with our python decorators and run it, we will encounter an error:-

@decorator_function
def b_function(name, age):
    print(f'{name} is {age} years old.')

b_function('Saral', 30)

TypeError: wrapper_function() takes 0 positional arguments but 2 were given

So, in order to create Python Decorators with arguments, we will have to add positional arguments (*args) and keyword arguments (**kwargs) to the wrapper function as under:-

def decorator_function(func):
    def wrapper_function(*args, **kwargs):
        print (f'Wrapper function ran before {func.__name__}.')
        return func(*args, **kwargs)
    return wrapper_function

@decorator_function
def b_function(name, age):
    print(f'{name} is {age} years old.')

b_function('Saral', 30)

Wrapper function ran before b_function.
Saral is 30 years old.

Real world example of a Python Decorator

To see the Python Decorators working in a real-world example, we will create a simple divide function, which will take two numbers as the argument and print the result:-

def simple_divide(a, b):
    print(a / b)

simple_divide(4,2)
simple_divide(2,4)

# Output
2.0
0.5

So, the above function is working as it should be and giving us the results, but if for some reason, you want the dividend to be always greater than the divisor, no matter what is the order of the passed arguments, you can create a if statement to switch the arguments. So, we can do it using the following code:-

def simple_divide(a, b):
    if b > a :
        a,b = b,a
    print(a / b)

simple_divide(2,4)

# Output
2.0

So now the question is, if we can easily do it from the code itself, why do we need the decorators. A few reasons for that could be a situation that you are working on a team project and you do not have access to this function or there are plenty of codes in this project, in which you want to add similar functionality. In such case, the decorators come handy. So, we will create our decorator as under:-

def my_decorator(func):
    def wrapper_function(a, b):
        if b > a:
            a, b = b, a
        return func(a, b)
    return wrapper_function


@my_decorator
def simple_divide(a, b):
    print(a / b)

simple_divide(2,4)

# Output
2.0

Creating a Timing Decorator in Python Decorators

One of the frequently used Python Decorators is the timer decorator. If in your project, you want to check out how much time each function is taking then instead of adding code to all the functions separately you can create a Python Decorator and then decorate all the desired function using that decorator. The code for creating the timing decorator in Python is as under:-

def timer(func):
    import time

    def wrapper_function(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f'{func.__name__} took {end-start} sec(s) to run.')
    return wrapper_function

import time

@timer
def a_function():
    time.sleep(1)
    print('A function ran.')

a_function()

# Output

A function ran.
a_function took 1.0029809474945068 sec(s) to run.

Creating a logging/logger decorator in Python.

Say, you have recently learnt the logging and now you are excited and apply it to all your fucntions in a project. You can easily do that using Python Decorators. The code for creating a Logging Decorator in Python is as under:-

def my_logger(func):
    import logging
    logging.basicConfig(filename=f'{func.__name__}.log', level=logging.INFO)

    def wrapper_function(*args, **kwargs):
        logging.info(f'Ran with args: {args} and kwargs: {kwargs}')
        return func(*args, **kwargs)

    return wrapper_function

@my_logger
def b_function(name, age):
    print (f'{name} is {age} years old.')

b_function()

Running this will create a new log file b_function.log. Check out our complete tutorial on Python Logging.

Adding more than one decorators in Python

You can also add more than one decorators in Python as under:-

@my_logger
@timer
def a_function():
    time.sleep(1)
    print('A function ran.')

a_function()

But running this will print the statement and then create a logging file with the name wrapper_function.log. This is not what we want. This happens because the above statement is equivalent to

my_logger(timer(a_function))

And since timer(a_function) returns the wrapper_function, the resultant log file is created with it’s name.

To overcome this issue, we will have to import another wrapper function called wraps and then wrap our decorator functions using that.

from functools import wraps

def timer(func):
    import time

    @wraps(func)
    def wrapper_function(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f'{func.__name__} took {end-start} sec(s) to run.')
    return wrapper_function


def my_logger(func):
    import logging
    logging.basicConfig(filename=f'{func.__name__}.log', level=logging.INFO)

    @wraps(func)
    def wrapper_function(*args, **kwargs):
        logging.info(f'Ran with args: {args} and kwargs: {kwargs}')
        return func(*args, **kwargs)

    return wrapper_function

How to create a Python Decorator Class?

Instead of creating a Python Decorator Function, you can also create a Python Decorator Class as under:-

class decorator_class(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print(f'Call Method ran before {self.func.__name__}.')
        return self.func(*args, **kwargs)

@decorator_class
def a_function():
    print ('A function ran.')

a_function()

# Output
Call Method ran before a_function.
A function ran.

Video Tutorial on Python Decortors

Don’t forget to check out our detailed video tutorial on Python Decorators



Related Posts

How to host and schedule your python script on PythonAnywhere?
By Udit Vashisht

In our last post here, we learned how to create a python script which will automatically delete your messages from gmail account based on a query.

The complete ready-to-use code can be found here.

Follow the steps in the above tutorial to download the ‘credentials.json’ file and ...

Read More
Matplotlib Candlestick Chart in Python | Matplotlib Tutorial | Chapter 11
By Udit Vashisht

How to create a Matplotlib Candlestick Chart in Python?

A candlestick chart or Japanese candlestick chart is a financial chart used to depict the price movement of securities, derivatives etc. in financial market. We can create a Matplotlib Candlestick Chart using a module called mpl_finance, which consists of ...

Read More
Python unittest module - How to test your python code?
By Udit Vashisht

Python unittest module

Why to unit test your python source code?

All programmers want their code to be impeccable, but as the saying goes, to err is human, we make mistakes and leave bugs in our source code. Here is where the unit testing comes to our rescue. If you ...

Read More
Search
Tags
tech tutorials automate python beautifulsoup web scrapping webscrapping bs4 Strip Python3 programming Pythonanywhere free Online Hosting hindi til github today i learned Windows Installations Installation Learn Python in Hindi Python Tutorials Beginners macos installation guide linux SaralGyaan Saral Gyaan json in python JSON to CSV Convert json to csv python in hindi convert json csv in python remove background python mini projects background removal remove.bg tweepy Django Django tutorials Django for beginners Django Free tutorials Proxy Models User Models AbstractUser UserModel convert json to csv python json to csv python Variables Python cheats Quick tips == and is f string in python f-strings pep-498 formatting in python python f string smtplib python send email with attachment python send email automated emails python python send email gmail automated email sending passwords secrets environment variables if name == main Matplotlib tutorial Matplotlib lists pandas Scatter Plot Time Series Data Live plots Matplotlib Subplots Matplotlib Candlesticks plots Tutorial Logging unittest testing python test Object Oriented Programming Python OOP Database Database Migration Python 3.8 Walrus Operator Data Analysis Pandas Dataframe Pandas Series Dataframe index pandas index python pandas tutorial python pandas python pandas dataframe python f-strings padding how to flatten a nested json nested json to csv json to csv python pandas Pandas Tutorial insert rows pandas pandas append list line charts line plots in python Django proxy user model django custom user model django user model matplotlib marker size pytplot legends scatter plot python pandas python virtual environment virtualenv venv python python venv virtual environment in python python decorators bioinformatics fastafiles Fasta