DigitalOcean Referral Badge
Udit Vashisht
Author: Udit Vashisht


Python Decorators - What are Python Decorators - A Complete Tutorial

  • 7 minutes read
  • 1012 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

Python Tutorial for Beginners
By Udit Vashisht

Python Tutorial for Beginners

Have you heard a lot about Python Language? Are you looking for free and reliable resource to learn Python? If Yes, your search for the Best Python Tutorial is over.

We are excited to bring an exhaustive Python tutorial for a complete beginner. Even ...

Read More
Matplotlib Stack Plots/Bars | Matplotlib Tutorial in Python | Chapter 4
By Udit Vashisht

Matplotlib Tutorial in Python

*Matplotlib Stack Plots/Bars | Chapter 4

In this chapter we will learn to create Matplotlib/Python Stack Plots/Bars.Stack Plots are also called area graphs/plots in general.

Matplotlib Tutorials in Python - Creating a Simple Stack Plot in Matplotlib

A good example of using a stack ...

Read More
Getting Started with Python: A Comprehensive Guide to Learning Python Programming
By Udit Vashisht

Introduction

Python is a versatile and beginner-friendly programming language that has gained immense popularity due to its simplicity, readability, and extensive community support. Whether you’re an aspiring programmer or looking to expand your skill set, learning Python can be a rewarding journey. This article aims to provide a comprehensive guide ...

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 python list append append raspberry pi editor cron crontab Cowin Cowin api python dictionary Python basics dictionary python list list ios development listview navigationview swiftui ios mvvm swift environmentobject property wrapper @State @Environm popup @State ios15 alert automation instagram instaloader texteditor youtubeshorts textfield multi-line star rating reusable swift selenium selenium driver requests-html youtube youtube shorts python automation python tutorial algo trading nifty 50 nifty50 stock list nifty50 telegram telegram bot dictionary in Python how to learn python learn python