Python Advanced Topics

Python Decorators: How to Use it and Why?

Python Decorator is a strong and helpful tool in Python that allows programmers to change the behavior of a function or class. Python functions are first-class citizens. This implies that they can be supplied as an argument, returned from a function, updated, and assigned to a variable. This is an important idea to grasp before diving into Python Decorator.

Python Decorators are useful when you wish to add logging, test performance, conduct caching, validate permissions, and so on. In this post, we’ll look at how to make and use Python decorators.

Decorators in Python

Python has an intriguing feature called decorators that allows you to add functionality to existing programs. This is also known as metaprogramming since a portion of the program attempts to modify another portion of the program during the compilation process. Python Decorator is a programming design pattern that dynamically adds new responsibilities to an object.

Decorators allow us to wrap another function around another function in order to extend the behavior of the wrapped function without permanently changing it. In Python, a decorator is a function that takes another function as an argument and extends its functionality without explicitly modifying the original function.

Prerequisites for learning decorators

To comprehend decorators, we must first understand a few fundamental Python concepts. We must accept the fact that everything in Python is an object. Functions are not exceptions; they are also objects (with attributes). As a result, a function can be assigned to a variable. That variable can be used to access the function.

                    

def myfunc():
    print('Python Programming')

print(myfunc())
x = myfunc
print(x())

Output

                    

Python Programming
Python Programming

Functions can also be provided as parameters to other functions. Higher-order functions are functions that take other functions as parameters. For example,

                    

def myself(func):
    func()
    print('I also love to code in Python')

def hobby():
    print('My hobby is to Read Books')

# Calling the myself function with the hobby function used as an argument
print(myself(hobby))

Output

                    

My hobby is to Read Books
I also love to code in Python

A function can also return another function i.e. nesting of functions.

                    

def outer_func():
    def inner_func():
        print('Inner function called by outer function')
    return inner_func

x = outer_func()
print(x())

Output

                    

Inner function called by outer function

In the above program, inner_func() is a nested function that is defined and returned each time outer_func() is called.

Getting back to Decorators

Because functions and methods can be called, they are referred to as callable. In fact, a callable object is any object that implements the specific __call__() method. In its most basic form, a decorator is a callable that returns another callable. A decorator basically takes a function, adds some functionality to it, and returns it.

In order to write a decorator function in Python, we must first write an outer function that accepts a function as an input. In addition, there is an inner function that wraps around the decorated function.

The syntax for a basic Python Decorator is as follows –

                    

def my_decorator_func(func):
    def wrapper_func():
         # Do something before the function.
         func()
         # Do something after the function.
    return wrapper_func

Example

                    

def my_decorator_func(func):
    def wrapper_func():
        # Do something before the function.
        print("I got decorated")
        func()
        # Do something after the function.
    return wrapper_func

def ordinary():
    print("I am ordinary")

# let's decorate this ordinary function
d = my_decorator_func(ordinary)
print(d())

Output

                    

I got decorated
I am ordinary

The my_decorator_func() is a decorator in the above example. The function ordinary() was decorated during the assignment stage, and the resultant function was given the name “d.” The decorator function, as we can see, provided some new functionality to the original function. This is analogous to wrapping a present. The decorator serves as a container. The nature of the object that was adorned (the real present within) remains unchanged. However, it presently appears to be in good condition (since it got decorated).

Python also has a special way to declare a decorator in the program.

Python makes it much easy for us to use decorators. We use decorators by putting the decorator’s name exactly above the function we want to use it on. The decorator function is preceded by a @ sign.

Example

                    

@my_decorator_func
def ordinary():
    print("I am ordinary")

Is equivalent to

def ordinary():
    print("I am ordinary")
d = my_decorator_func(ordinary)

Decorating Functions with Parameters

The above decorator example was straightforward, and it only worked with functions that had no parameters. What if we had functions that could be passed parameters? Let us make a decorator for a case where the function accepts 2 arguments for division operation.

Example

                    

def divide(func):
    def inner(a, b):
        print('Dividing', a, 'and', b)
        if b == 0:
            print('Whoops! Not Divisible')
            return

        return func(a, b)
    return inner

@divide
def div(a, b):
    print(a/b)

print(div(10, 5))
print(div(4, 0))

Output

                    

Dividing 10 and 5
2.0
Dividing 4 and 0
Whoops! Not Divisible

A keen observer will notice that the parameters of the decorator’s nested inner() function are the same as the parameters of the functions it decorates. Taking this into consideration, we can now create general decorators that can function with any amount of parameters. So, add *args and **kwargs to the inner routines to add arguments to decorators.

  • *args accepts an infinite number of arguments of any type, such as 10, True, or ‘Brandon.’
  • **kwargs can accept an infinite number of keyword arguments, such as count=99, is_authenticated=True, or name=’Brandon.’

Example

                    

def for_all(func):
    def inner(*args, **kwargs):
        print('Decorate any function')
        return func(*args, **kwargs)
    return inner

Chaining Decorators in Python

In Python, many decorators can be linked together. A function can be decorated with various (or the same) decorators several times. The decorators are simply placed above the required function.

Example

                    

# Python program to illustrate decorator chaining
def square(func):
            def inner():
                        x = func()
                        return x * x
            return inner

def multiply(func):
            def inner():
                        x = func()
                        return 2 * x
            return inner

@square
@multiply
def num():
            return 10
print(num())

Output

The order in which we chain decorators is significant. If we had reversed the sequence, it would have been as follows:

                    

@multiply
@square
def num():
           return 10

print(num())

Output

Built-in Decorators

You can not only define your own decorators but there are also those included in the standard library. @classmethod, @staticmethod, and @property are some of the most often used decorators built into Python.

The @classmethod and @staticmethod decorators are used to declare methods within a class namespace that are not associated with a specific instance of that class. The @classmethod and @staticmethod decorators convert method functions to class-level functions. The decorated method can now be invoked from a class rather than an object.

The @property decorator is used to alter class attribute getters and setters. A method function is transformed into a descriptor by the @property decorator. This is used to provide a method function with the syntax of a simple attribute.

Frequently Asked Questions

Q1. How do you write decorators in Python?

Decorators allow us to wrap another function around another function in order to extend the behavior of the wrapped function without permanently changing it. In Python, a decorator is a function that takes another function as an argument and extends its functionality without explicitly modifying the original function.

In order to write a decorator function in Python, we must first write an outer function that accepts a function as an input. Also, there is an inner function that wraps around the decorated function. The syntax for a basic python decorator is as follows –

                    

def my_decorator_func(func):
    def wrapper_func():
         # Do something before the function.
         func()
         # Do something after the function.
    return wrapper_func

Python also has a special way to declare a decorator in the program. We use decorators by putting the decorator’s name exactly above the function we want to use it on. The decorator function is preceded by a @ sign.

                    

@my_decorator
def hello_decorator():
    print('Python Programming')

Q2. Why do we need decorators in Python?

Python Decorator is a strong and helpful tool in Python that allows programmers to change the behavior of a function or class. A decorator is a programming design pattern that dynamically adds new responsibilities to an object.

It’s as easy as that: readability. Decorators are no exception to Python’s reputation for clean and concise syntax. If there is any behavior that is shared by more than one function, you should probably create a decorator. Here are some examples of when they might be useful:

  • At runtime, check the type of parameters.
  • Function calls to be benchmarked
  • Cache function output
  • Function calls should be counted.
  • Examine the metadata (permissions, roles, etc.)
  • Metaprogramming
Share with friends

Customize your course in 30 seconds

Which class are you in?
5th
6th
7th
8th
9th
10th
11th
12th
Get ready for all-new Live Classes!
Now learn Live with India's best teachers. Join courses with the best schedule and enjoy fun and interactive classes.
tutor
tutor
Ashhar Firdausi
IIT Roorkee
Biology
tutor
tutor
Dr. Nazma Shaik
VTU
Chemistry
tutor
tutor
Gaurav Tiwari
APJAKTU
Physics
Get Started

Leave a Reply

avatar
  Subscribe  
Notify of

Download the App

Watch lectures, practise questions and take tests on the go.

Customize your course in 30 seconds

Which class are you in?
No thanks.