Analytics Vidhya
Published in

Analytics Vidhya

Python Decorators

In this article we’ll learn and understand what a Python decorator is.

Photo by Sunil Rana on Unsplash

Been in my draft for at least 8 months so completing now. 🌅

While working with a Django codebase, have come across multiple usage of decorators, especially in user authorization parts. Had somewhat working understanding of how decorators work with patterns I had seen and used in the codebase. Hence, deep diving on what actually decorators are.

Decorators are functions which modify the functionality of other functions. They help make our code shorter (DRY).

Before diving into decorators, let’s make a concept of ‘functions in Python as first-class citizen’ clearer. In programming language design, ‘first-class citizen’ implies being able to be

  • assigned to a variable
  • passed as an argument
  • returned from a function

Let’s see these behaviors with an example:

0. functions in python can be assigned to a variable:

A newbie me would look at the assignment curiously, thinking why isn’t there () after a method name square . Well, that is because we are not executing the square method. We are instead passing a reference of the method to a new variable named get_square. Hence, we can now call the method square with the new variable get_square.

  1. functions in python can be passed as an argument

Here we passed the function square ‘s reference as an argument to the function cube , execute the square function, and multiply the value returned by it again by number_to_cube to get cube of a number! Nifty.🙂

2. functions in python can return another function

Let’s take a step back here, and see that we can define a function into another function.

Here we have defined a function multiply_by_two inside a function double_func . We called the multiply_by_two function inside the double_func function and returned it’s value. So we see we can define a function inside another function.

If you have noticed, the function multiply_by_two uses number argument passed to the double_func function. This is because nested functions have access to enclosing function’s variable scope.

Now let’s see that we can return a function as well.

Here we see double_func function is assigned to a variable doubled . We can call doubled by using the () because return value of double_func is a function! If you have noticed, we used the first property of a first-class citizen; function can be assigned to a variable.

You made it to the midway of the article. Congrat! 👏

Now since we have necessary knowledge required to understand decorators, let’s dive into it!

Let’s create a simple decorator:

Here we passed say_bye function as a parameter to a function lowercase_decorator which has a function wrapper inside it. The wrapper function executes the say_bye function and returns its lower cased returned value .

This is somewhat too much to write, so Python has presented the succinct decorator.

We can implement the above functionality by just using a @lowercase_decorator on the say_bye function, like this:

See we just needed to use @<name-of-the-decorator> on top of a function we want to use the decorator on.

We can also apply multiple decorators to a function.

See, we have two decorators: first one replaces space with dash, and another converts string to a lowercase. Here, order of decorator does not matter because we can change the order and still get the desired result. However, this might not always be the case.

For example, if we had a decorator that splits a string instead of replacing space with dash, then we need to have first decorator to be lower casing a string and second to be splitting the string. This is because splitting a string returns a list data type, which cannot be lowercased. Let’s take a 3/4th of an article breather 🍃

Here we’ll learn about how we can pass arguments to a decorator:

Here arguments are passed to the wrapper function which is inside the decorator. This is done during execution time.

Let’s define a more general purpose decorators with *args and **kwargs :

Let’s recall what *args and **kwargs are. *args stands for variable number of arguments and **kwargs stands for variable number of keyword arguments. Keyword arguments has a syntax of key=val and args just has a simple variable name like var. It is specially useful when we do not know what number of arguments will be passed to a function. Hence, general-purpose. 🙂

Here, as expected, the behavior is similar to the previous example of passing arguments to a wrapper inside a decorator but here we have a flexibility of sending any number of arguments including none.

Now let’s explore what if we need to send arguments to a decorator itself!

Well, nothing much here as well. We needed an extra decorator that takes decorator arguments and the rest two inner function’s structure is same as the previous one.

Ok, now we see so many nested function. What if we want to debug them? Let’s see what meta data do we get of the function places_to_travel .

print(places_to_travel.__name__) # prints 'wrapper'
print(places_to_travel.__doc__) # prints 'This is a wrapper function'

hmm.., this does not really help while debugging because we want to see which actual function the decorator is acting on. Luckily, we already have a libraryfunctools which we can decorate on a wrapper function passing argument of a function that it is wrapping, like this:

Now if we do

print(places_to_travel.__name__) # prints 'place_to_travel'
print(places_to_travel.__doc__) # prints 'This lists some of the country on my travel-list.'

As needed!

Now let’s recap what a decorator is. Decorator modifies functionality of function it is decorating on. This is possible due to functions in python being a ‘first-class’ citizen.

Congratulation on the completion! You made it 💪. I hope it was helpful for both understanding and implementation of python decorators.

My next article most probably will be on Java annotation. See you then.

Thank you for reading.

If you liked it, 👏 appreciated.

Reference:

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store