Decorators in Python

Probhakar
5 min readJun 26, 2021

--

Step by step guide to python decorators and how to use them

So you are reading this post which means you already have some knowledge about python and some vague ideas about decorators. Let’s walk through this article, we hope your idea about decorator will be much more solid by the end of this article. bonus we will try to create the decorator “app.route(‘/’)” you used to see in Flask from scratch.

So what is a decorator and why do we need this?

Decorators as the name suggests are a way to decorate any python object and replaces the old variable pointing to the original object with the newly modified object.

There is too much, let’s see it in a convenient way. Suppose there is an object named snow_queen. Now snow queen does not have wings. So we will create a decorator that decorator will add the wings to the snow_queen.

You might be thinking, I should have used a class for SnowQueen then use snow_queen object, but remember decorators can also be used with class or anything. And functions are also “object” in python.

The above picture shows what is a decorator. You might be thinking then where is the @decorator sign you are accustomed to seeing. Actually, the @decorator sign came later to python, it is just syntax sugar. Under the hood what you are doing is —

  1. Create a decorator i.e. a callable object (a function or a class implementing __call__ magic method) that takes an object and returns an object.
  2. You call the decorator callable object with the object(remember functions in python are also object so I am using “object”) you want to modify and replace the variable name with the new modified object. For example, here I am replacing the snow_queen with the new modified object that is actually the wrapper function that the give_wing function returns. Now, whenever you will call the snow_queen function you will actually call the wrapper function.
using @ syntactic sugar

The output is —

Output

Here I am using inspect built-in module to get the source code as a string of any python object. It is a neat tool to see the source code of a python object.

Now there are some cases where you want to pass some variables to the decorator method. For example, you want to tell the color of the wings, depending on what the decorator will give the wings. Like you want to do something like —

parameterized decorator

To achieve this we need a higher-order function (higher-order function that takes a function as a parameter or returns a function or do both, decorators are higher-order functions) that will —

  1. take an argument and will return a function (suppose A)
  2. we should be able to call A with the object we want to decorate and A should return a modified object

So you understand that we need 3rd function that will take the arguments and return an actual decorator. It is sometimes called a decorator factory since it returns decorator.

parameterized decorator

So the implementation will be

function implementation

Using class —

class with __call__ method

Now you might have noticed that decorator actually replacing the original function signature with the modified function. That may be problematic if you are doing some operation that depends on the function name.

Suppose you have invited many people to your party and you have a list of all the people. Now at the gate, you are checking if the invited people are on the list, if not you are rejecting the entry. Now let's see without a decorator how it will work —

your party

As you can see, snow_queen is welcomed to your party. Now suppose snow_queen gets her wing before coming to your party, what will happen —

your party

Alas! it is saying “Stop wrapper, you are not invited”. But should not it be snow_queen? You can remember that what decorator is to replace the original variable with the new modified object so effectively the function signature gets changed. So is there any way to solve this problem?

You might be thinking to replace the __name__ attribute of the returned function from the decorator. Let’s see if it works —

your party

Holla, we have solved the problem. But in a real scenario, there are many things to replace apart from __name__. These are listed in functools

6 things to replace

These are the 6 things you need to update. But fortunately, you don’t have to do it manually since “functools” has a method called wraps that exactly does these things.

using functools.wraps

“wraps” basically changes the signature of wrapper function with the signature of “func” i.e. “snow_queen” in this case.

Now that you know a lot about decorators, let’s explore whether we can make Flask like decorators or not —

Flask like decorator

The output is —

--

--