The world’s leading publication for data science, AI, and ML professionals.

Python Decorator: What, Why, and How

Python decorator is a very useful tool to help us code efficiently. As I mentioned in my previous article, coding efficiently is one of the…

Python decorator is a very useful tool to help us code efficiently. As I mentioned in my previous article, Coding efficiently is one of the principles I follow to be a better data scientist:

Seven Principles I Follow To Be a Better Data Scientist

In this article, I want to uncover the use cases of Python decorators and discuss how they can be helpful for your daily programming tasks.


What is Python Decorator

The name of Python decorator might sound fancy, but in essence, it’s no more than a tool you can use to customize the output of an existing function without modifying the function itself. It is called "decorator" because you can apply the same concept to as if you are wrapping gifts. We can decorate a gift with different wrappers for different holidays. Even though the gift itself is the same, we can send it on different occasions by decorating it differently. Applying this metaphor to python programming language, the gift is a python function, the wrappers Python Decorators, and your tasks are different holidays. In this article, I will discuss two major use cases of python decorators: one is to modify a function, and another one is the cache the function outputs to save future runtime. We will see the two use cases with examples in the following section.


Why and how to use Python Decorator

1 – Modify a function output without modifying the function itself

Before discussing how to modify a function with a Python decorator, let’s start with a simple function:

def add_five(x):
    return x+5

This function will simply add five to the input x. For simplicity, let’s assume x is numerical.

We can see when you call the function, it produces an output adding five to the input. It is a very simple function with a straightforward operation. Now, what if we want to use this function but apply a condition to the output saying that the output cannot be larger than 10? We can have the following function added on top of the add_five() function:

def ensure_ceiling(func):
    ceiling = 10
    def wrapper(x):
        value = func(x)
        return value if value < ceiling else x
    return wrapper

The ensure_ceiling() function takes a function as input. It first defines a ceiling at 10. Then define a wrapper function, which takes the input function’s input x and produces an output value. Then wrapper function’s output will be modified using the ceiling condition. Finally, the ensure_ceiling function returns the wrapper function as an output. We can then modify the add_five function by putting it as the input of the ensure_ceiling function. Let’s see how it works:

Now, if we put input as 3, the ensure_ceiling function will use the wrapper function inside to produce a value, which is add_five(3). The output of the wrapper function is 8 since the 8 is smaller than ceiling at 10, which is also what ensure_ceiling(add_five) would return. So far it works like add_five(). What if the initial input we give is 6? Then add_five(6) would be 11, which is higher than the ceiling at 10. So instead of returning 11, it will return the input itself, 6:

We haven’t really used the decorator here, even though we have defined the decorator function. The most efficient way of using it is by the following code:

After writing the ensure_ceiling function, we can "wrap" it directly above the function we want to decorate with an "@" sign. Once applied, we can call the decorated function directly to get the modified output.

2 – Cache previous run results

Decorators can also work as a function-level cache. Using the same add_five() function, we can save the output each time we run the function, so when the same input is given later, we can quickly grab the results without running the function again. This is super helpful, especially when the function takes a long time to run and will be reused. Let’s see the example here. We first define the decorator:

def ensure_ceiling_cache(func):
    ceiling = 10
    cache = {}
    def wrapper(x):
        if x in cache.keys():
           print(f'Using cache for {x}')
        else:
           print(f'Using function for {x}')
           value = func(x) if func(x) < ceiling else x
           cache[x] = value
     return cache[x]
 return wrapper

Inside the decorator, we include a "cache" dictionary to save the output from each run, then define an if/else statement to decide whether we need to run the function to get the results, or simply use the cached dictionary. Let’s see the use of it in add_five():

Now when we call the add_five() function, not only will it apply a ceiling for the output, but it also has a cache dictionary saved inside the decorator. When calling add_five(3) the second time, it will not use the function to produce the results, but simply find what the previous results are.


In summary, we have used the python decorator to modify an existing function and cache the previous outputs to save time when running a function repetitively. Hope the use cases with the examples are straightforward to understand. What are the other scenarios you have encountered using python decorators? Let us know in the comment below!

Thank you for reading! Here are some other articles I have written about Python Programming:

I also have a list of data scientist interview preparation questions about machine learning, statistics, probability, case study, and behavior questions, check them out if needed.

There are also articles of mine if interested:

My Blog Posts Gallery

Subscribe to my email list:

Get an email whenever Zijing Zhu publishes.

Sign up for medium membership:

Join Medium with my referral link – Zijing Zhu

Or follow me on YouTube:

Zijing Zhu


Related Articles