Decorators in Python: 6 Lessons I Learned the Hard Way
I will be frank that the first time I collided with the idea of decorators in Python, I did not understand what it was all about.
Table Of Content
This was like Python had thrown me into some sort of magic show where functions were enclosed in other functions and I was not able to determine what was being done.
The thing is though that after it clicked I realized that decorators in python are one of the most powerful tools that you will ever employ.
Imagine this:
👉 You have a function.
👉 You desire logging, authentication or timing.
👉 Instead of rewriting the whole function, you just wrap it with a decorator.

1. What Are Decorators in Python?
Let’s start with the basics.
Decorators Python decorators are functions that act on another function (or even a class) and alter its behavior, without making any permanent changes.
Think of it like this:
-
You have a plain cake 🍰 (your function).
-
You add icing and sprinkles (the decorator).
-
The cake is still the same inside, but it’s now tastier and prettier.
That’s what decorators do in Python—they add functionality in a clean, reusable way.
Example:
def decorator_function(original_function):
def wrapper_function():
print("Before the function runs")
original_function()
print("After the function runs")
return wrapper_function
@decorator_function
def say_hello():
print("Hello, World!")
say_hello()
Output:
Before the function runs Hello, World! After the function runs
See what happened? The say_hello function was wrapped with additional functionality – without me having to alter the core of the functionality.

2. Why Use Decorators in Python?
The first time I did a web application in Flask (a Python web framework) I observed something. We did not rewrite code per page to gain routes. Instead, we used a decorator:
@app.route('/dashboard')
@login_required
def dashboard():
return "Welcome to your dashboard!"
The decorator login required immediately verified the presence of a user’s presence via the system-administered login process- no longer did I need to repeat the user logged in logic at every other spot.
That’s when I realized:
- Less code translators in Python = less code + cleaner code.
- And quite frankly, the smaller the code, the smaller the headaches.
3. How Decorators in Python Actually Work
Decorators in Python are based on two things:
- Functions are first-class objects (functions are movable around just like variables).
- Functions within functions.
Here’s the simple flow:
- You make a wrapper function.
- This wrapper is sent back by the decorator.
- The wrapper puts extra behavior in the pre- or post-call of the original function.
Example with arguments:
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello {name}!")
greet("Python")
Output:
Hello Python! Hello Python! Hello Python!
4. Types of Decorators in Python
There are several ways to use decorators in Python, and knowing them helps you decide which one to use in your project.
-
Function Decorators – the most common ones you’ll see.
-
Class Decorators – yes, you can wrap entire classes!
-
Built-in Decorators – like
@staticmethod,@classmethod, and@property.
Example of built-in decorator:
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
p = Person("Alice")
print(p.name)
Here, @property makes the method behave like an attribute.
5. Real-Life Uses of Decorators in Python
I use the decorators in Python everywhere. Here are some practical uses:
Logging – record the time functions are invoked.
Authentication – web application secure routes (such as my Flask example).
Performance Tracking – time a function is taken to execute.
Validation of inputs – verify inputs before the function is executed.
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} ran in {end - start:.4f} seconds")
return result
return wrapper
@timer
def process_data():
time.sleep(2)
print("Data processed")
process_data()
6. Common Mistakes Beginners Make with Decorators in Python
I’ve been there too. Here are mistakes I made:
-
Forgetting to return the inner function (your decorator will break).
-
Using decorators without understanding
*argsand**kwargs. -
Over-decorating functions (yes, you can go overboard).



