3 Interviews — 5 Questions (Part-II)

Ramsha Bukhari
4 min readDec 13, 2020

This is part II of three parts post about some basic questions that I learnt about by failing the interviews.

In case you find this interesting, please have a look at the first part here.

Decorator — Wrapper over an existing Function

What are Decorators?

For a relief, I believe if someone gets this question, that means you are already at level 2.
Decorator, as the name gives it away, is ‘something’ that wraps over the original ‘something’ to make it even better. But of course, this leads you to another question. How do we do it and Why do we need it?

How do we do it?

The explanation of this requires a ground understanding of the following facts about Python.

  1. Everything in Python is an object and so are functions. Meaning; declaring a function means writing a piece of code that is saved at a fixed memory address and then that address (a.k.a reference) can be passed around as the location of the function. Anyone who has this address can call the function regardless of where the function was originally defined.

2. Since functions are objects, they can be passed around as an argument to other functions and can also be returned from other functions as a return value.

  1. A function (lets call it F2) can also be defined inside another function (lets call it F1) and in this case F2 will be called an inner function. An inner function is scope limited so is only accessible inside the outer function but it can be returned as a return value.

If these things are clear, then we are all set to understand how we decorate the functions in python.

A Real-Life Analogy

Before diving head straight into the code, let’s go through this super simple analogy to understand how decorators work. Lets say you go to the airport check-in counter to board your flight and the staff issues you a boarding pass. For some reason we want to have a separate check to match the person’s name on his travel document and plane ticket but doing this for every person at the boarding counter means adding an extra delay time for the queue. So what we do instead is, we setup a separate counter for information verification. Anyone who wants to check-in must first go to the information verification desk, get their information verified and then will eventually be redirected to the check-in counter to get the boarding passes. In this way, we made no change in the work processes for the check-in counters but only added another desk to cater for our needs.

Real life analogy of a decorator

This is what exactly happens in the decorator function. In Python , anyone who wants to call a function uses the function name to make that call. This is because function name is actually the variable holding the memory address or location of the function. What we do here is, we pass this function name (and hence address) to another function of ours (a.k.a decorator). Inside decorator, we do the stuff we wanted to do and then eventually call the original function by using the address that was passed to the decorator function in the form of name of the function. When everything is done we return the decorator function back.

The below example will summarize all of this in terms of code.

Let’s say we have a function named Factorial which is used to calculate factorial of a given number.

This works perfectly but only if the number passed is a positive integer. For any negative value passed to this function, will result in an error. But let’s say you have a limitation of not being able to modify this function to cater for this value check. That is where a decorator will come into action.

If we look at line 20, we are passing our factorial function’s address to our decorator function, only so that when the decorator function is done doing what it needs to do, it knows which function to call after that.

At lines 10 -15 , we take in the factorial function, write our own function by the name of validation_function which performs the data validation checks and when its done, it calls the original factorial function to do its job and eventually we return the validation_function back. We can clearly see the validation_function is acting as a wrapper here to the original factorial function and hence the name decorator.

The Syntactic Sugar

Since Python is all about writing compact and hence aesthetically pleasing code, there is a simplified way of applying decorators to any function , we call this pie syntax. We can define our decorator function once and instead of making all those nested functions, we can add the decorator over as many functions as we need using the pie syntax.

Why do we need decorators?

Now that we are almost (hopefully!) clear on the working of decorator functions, we also need to mention what is the need to do this. Generically, decorators serve the purpose of meta programming i.e. modifying the behavior of original function without changing its implementation but some specific use cases that we can mention, include:

  1. Logging the execution time of a function(s)
  2. Asserting data authentication & validation
  3. Exception handling for similar functions
  4. Acquiring or releasing the lock in multi-threaded applications
  5. To handle successful database transactions and rolling back in case of a failure

The final part of this post will address the garbage collection mechanism in Python.

--

--

Ramsha Bukhari

A coder-cum-teacher! — The coder takes care of the ‘How’ while the teacher looks after the ‘Why’ —