Find out how HackerEarth can boost your tech recruiting

Learn more
piller_image

Getting started: Python Decorators

This post will help you get started with Python decorators through some real life examples. Some familiarity with the Python programming language is expected.

 

Before directly jumping into decorators, let’s take a step back and start with Python functions. This will help you understand the concepts better.

Functions

A function in Python can be defined as follows:

def introduce(name):
    return 'My name is %s' % name

This function takes name as input and returns a string, where:

  • def is the keyword used to define a function.
  • introduce is the name of the function.
  • the variable inside parentheses (name) is the required argument for the function.
  • next line is the body or definition of the function.

Function Properties

In Python, functions are treated as first-class objects. This means that Python treats functions as values. We can assign a function to a variable, pass it as an argument to another function, or return it as a value from another function.

def print_hello_world():
    print('Hello World!')

We have defined a function ‘print_hello_world’. Now we can assign it to a variable.

>>> modified_world = print_hello_world

(Here >>> is denoting the python interactive shell)

Now we can call modified_world just like the function print_hello_world.

>>> modified_world()
Hello World!

We can also pass a function to another function as an argument.

def execute(func):
    print('Before execution')
    func()
    print('After execution')

So now when we pass print_hello_world function to execute function, the output will be as follows:

>>> execute(print_hello_world)
Before execution
Hello World!
After execution

Python also supports the nesting of functions. It means we can define another function in the body or definition of some other function. Example:

def foo(x):
    def bar(y):
        return x+y
    return bar

In the example above, we have used two concepts described earlier.

1. Returning a function (bar) as a return value of the function foo

2. Nesting function bar in the definition of the function foo

Let’s see this code in action.

>>> v1 = foo(2)

Here v1 stores the return value of the function foo,which is another function bar. Now what will happen if we call v1 with some parameter?

>>>print(v1(5))
7

When a function is handled as data (in our case, return as a value from another function), it implicitly carries information required to execute the function. This is called closures in Python. We can check the closure of the function using __closure__ attribute of the function. This will return a tuple containing all the closures of the function. If we want to see any content of the closure, we can do something like v1.__closure__[0].cell_contents.

>>> v1.__closure__
(<cell at 0x7f4368e6c590: int object at 0xa41140>,)
>>> v1.__closure__[0].cell_contents
2

So, now that we looked at both function properties, let’s see how we can use these properties in real scenarios.

Going Ahead

Suppose we want to perform some generic functionality before or/and after function execution. It can be like printing the execution time of the function.

One way to do this is by writing whatever we want to do before and after execution as initial and final statements, respectively. Example:

def print_hello_world():
    print('Before Execution')
    print('Hello World!')
    print('After Execution')

Is this a good way. I leave it to you. What will happen if we have several functions and need to perform the same task for all other functions too?

Another way could be to write a function that will take any other function as an argument and return the function along with performing the task before and after function execution. Example:

def print_hello_world():
    print('Hello World')

def dec(func):
    def nest_func(*args, **kwargs):
        print('Before Execution')
        r =  func(*args, **kwargs)
        print('After Execution')
        return r
    return nest_func

The function print_hello_world just prints ‘Hello World’. Function dec takes a function as an argument and creates another function nest_func in its definition. nest_func prints some statements before and after the execution of the function is passed as an argument to function dec.

Let’s pass the function print_hello_world to dec.

>>> new_print_hello_world = dec(print_hello_world)

new_print_function is another function returned by the function dec. What will be the output on calling new_print_hello_world function? Let’s check it.

>>> new_print_hello_world()
Before Execution
Hello World
After Execution

What if we assign the new function returned by the dec function to print_hello_world function again?

>>> print_hello_world = dec(print_hello_world)

Let’s call print_hello_world function now.

>>> print_hello_world()
Before Execution
Hello World!
After Execution

We have changed the functionality of the function print_hello_world without changing the source code of the function itself.

So what next? If everything is clear till this point, then we have already learned about decorators. Let me explain.

Decorators

A decorator is a function which gives us the freedom to enhance or change the functionality of any function dynamically, without making changes in the code of the function.

In our case, function dec provides us with this functionality (as it changes the functionality of the function print_hello_world). So dec is called decorator. Instead of passing print_hello_world explicitly to function dec, we can use its shorthand syntax:

@dec
def print_hello_world():
    print('Hello World')

I hope by now you understand what decorators are. You might be wondering why we need to return a function from the dec function? Just call the function in dec itself in which we can print statements along with executing the function passed as an argument. Example:

def dec1(func):
    print('Before Execution')
    func()
    print('After Execution')

I have a few questions for you in answer. Suppose, I agree with you and decide to do it as suggested.

>>> print_hello_world = dec1(print_hello_world)
  1. What value does print_hello_world store right now? Can you call it now? (It is storing None which is the return value of the function dec1. So you can’t call print_hello_world now)
  2. What if we want to enhance a function having some arguments? One suggestion could be like this:
def dec2(func, arg1, arg2):
    print('Before Execution')
    func(arg1, arg2)
    print('After Execution')

But the problem is this: how do you get the value of arg1 and arg2 at the time of passing any function to dec2?

>>> print_hello_world = dec2(print_hello_world, arg1, arg2)

Here, we will not be able to get the value of arg1 and arg2.

I hope these two points clearly explain why decorators are required to return a function.

Decorator Examples

  • It can be used to compute the execution time of any function.
    def compute_execution_time(func):
        def nest_func(*args, **kwargs):
            start = time.time()
            response = func(*args, **kwargs)
            end = time.time()
            print(end-start)
            return response
        return nest_func
  • In web applications, it can be used to check if the user is logged in or not.
    def login_required(func):
        def nest_function(request, *args, **kwargs):
            if request.user.is_authenticated():
                return func(request, *args, **kwargs)
            else
                return redirect('/login')
        return nest_function

I hope this article gives you a basic idea about Python decorators and some of their use cases. If you have any queries or feedback, you can reach me at udr.ritesh@gmail.com.

Hackerearth Subscribe

Get advanced recruiting insights delivered every month

Related reads

Best Interview Questions For Assessing Tech Culture Fit in 2024
Best Interview Questions For Assessing Tech Culture Fit in 2024

Best Interview Questions For Assessing Tech Culture Fit in 2024

Finding the right talent goes beyond technical skills and experience. Culture fit plays a crucial role in building successful teams and fostering long-term…

Best Hiring Platforms in 2024: Guide for All Recruiters
Best Hiring Platforms in 2024: Guide for All Recruiters

Best Hiring Platforms in 2024: Guide for All Recruiters

Looking to onboard a recruiting platform for your hiring needs/ This in-depth guide will teach you how to compare and evaluate hiring platforms…

Best Assessment Software in 2024 for Tech Recruiting
Best Assessment Software in 2024 for Tech Recruiting

Best Assessment Software in 2024 for Tech Recruiting

Assessment software has come a long way from its humble beginnings. In education, these tools are breaking down geographical barriers, enabling remote testing…

Top Video Interview Softwares for Tech and Non-Tech Recruiting in 2024: A Comprehensive Review
Top Video Interview Softwares for Tech and Non-Tech Recruiting in 2024: A Comprehensive Review

Top Video Interview Softwares for Tech and Non-Tech Recruiting in 2024: A Comprehensive Review

With a globalized workforce and the rise of remote work models, video interviews enable efficient and flexible candidate screening and evaluation. Video interviews…

8 Top Tech Skills to Hire For in 2024
8 Top Tech Skills to Hire For in 2024

8 Top Tech Skills to Hire For in 2024

Hiring is hard — no doubt. Identifying the top technical skills that you should hire for is even harder. But we’ve got your…

How HackerEarth and Olibr are Reshaping Tech Talent Discovery
How HackerEarth and Olibr are Reshaping Tech Talent Discovery

How HackerEarth and Olibr are Reshaping Tech Talent Discovery

In the fast-paced tech world, finding the right talent is paramount. For years, HackerEarth has empowered tech recruiters to identify top talent through…

Hackerearth Subscribe

Get advanced recruiting insights delivered every month

View More

Top Products

Hackathons

Engage global developers through innovation

Hackerearth Hackathons Learn more

Assessments

AI-driven advanced coding assessments

Hackerearth Assessments Learn more

FaceCode

Real-time code editor for effective coding interviews

Hackerearth FaceCode Learn more

L & D

Tailored learning paths for continuous assessments

Hackerearth Learning and Development Learn more