Udit Vashisht
Author: Udit Vashisht


Python Decorators - What are Python Decorators - A Complete Tutorial

  • 7 minutes read
  • 10 Views
Python Decorators - What are Python Decorators - A Complete Tutorial

    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

Matplotlib Subplot in Python | Matplotlib Tutorial | Chapter 10
By Udit Vashisht

Matplotlib Subplot in Python | Chapter 10

In this Matplotlib Subplot tutorial, we will be learning to create Matplotlib Subplots. Till now, we have been using matplotlib.pyplot() to create the plots. But now, we will be using matplotlib.pyplot.subplots() (ptl.subplots()) to create Matplotlib Subplots.

Matplotlib.pyplot() or plt was automatically ...

Read More
Venv Python - A complete tutorial on Virtual Environments in Python
By Udit Vashisht

What is Virtual Environment in Python ?

Virtual Environment is a kind of a container which runs specific version of Python and its modules. And it is used in the cases where you are working on two different project which has different dependencies. Say one of your project uses Django ...

Read More
How I used Python and Web Scrapping to find cheap diaper deals?
By Udit Vashisht

There were two things which pushed me to write down this code:-
1. Diapers are expensive and saving a dollar or two on it every month is cool.
2. If you are not using python to automate certain stuff, you are not doing it right.

So, here is ...

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