Decorators in Python: If you’re coding in Python, chances are you’ve already used decorators—perhaps without even realizing it. Whether you’re a beginner just discovering functions or an advanced Pythonista brushing up on best practices, decorators are a concept you must understand deeply.
This article walks you through Decorators in Python, starting from the basics and gradually moving to advanced use cases, so you come out the other side ready to wield them like a pro.
Table of Contents
🎯 What is a Decorator in Python?
A decorator in Python is a function that modifies the behavior of another function (or method or class) without changing its source code.
Think of decorators as wrappers that add extra functionality to existing code in a clean, readable way.
Basic Structure:
def decorator_function(original_function):
def wrapper_function():
print("Wrapper executed before the original function.")
return original_function()
return wrapper_function
Apply it to a function:
@decorator_function
def say_hello():
print("Hello!")
say_hello()
Output:
Wrapper executed before the original function.
Hello!
🔍 Why Use Decorators?
- ✅ Code Reusability – You don’t need to copy/paste logic across multiple functions.
- ✅ Separation of Concerns – Keeps logic modular (e.g., logging, timing, access control).
- ✅ Cleaner Syntax – The @decorator syntax is elegant and improves readability.
- ✅ Meta-programming – Allows you to modify behavior at runtime.
🛠️ Creating Your First Custom Decorator
Let’s say you want to log every time a function is called.
def logger(func):
def wrapper(*args, **kwargs):
print(f"Function '{func.__name__}' was called with args: {args}, kwargs: {kwargs}")
return func(*args, **kwargs)
return wrapper
@logger
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Output:
Function ‘greet’ was called with args: (‘Alice’,), kwargs: {}
Hello, Alice!
🎛️ Decorators with Arguments
Need to pass parameters to your decorator? That requires three levels of nested functions.
def repeat(num_times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_hi():
print("Hi!")
say_hi()
Output:
Hi!
Hi!
Hi!
🧠 Advanced Usage: Built-in Decorators and functools
1. @staticmethod and @classmethod
class MyClass:
@staticmethod
def static_method():
print("I don't need class or instance.")
@classmethod
def class_method(cls):
print(f"I got the class: {cls}")
2. Using functools.wraps
One common mistake is forgetting to preserve metadata (like __name__, __doc__) when wrapping a function.
import functools
def debug(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
Without @functools.wraps, the decorated function would lose its original name and docstring.
🧪 Real-World Use Cases of Decorators
✅ Logging
def log_execution(func):
def wrapper(*args, **kwargs):
print(f"{func.__name__} is executing...")
result = func(*args, **kwargs)
print(f"{func.__name__} execution finished.")
return result
return wrapper
✅ Timing
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.4f} seconds")
return result
return wrapper
✅ Access Control / Authentication
def requires_admin(func):
def wrapper(user, *args, **kwargs):
if not user.get('is_admin'):
raise PermissionError("Admin access required.")
return func(user, *args, **kwargs)
return wrapper
🧵 Stacking Multiple Decorators
You can apply more than one decorator to a single function:
@timer
@logger
def process_data(data):
time.sleep(1)
return f"Processed {data}"
Decorator order matters! The bottom one is applied first.
⚠️ Common Pitfalls
- Forgetting to return the wrapped function.
- Ignoring the importance of @functools.wraps.
- Overusing decorators—sometimes simpler solutions are better.
📚 Summary: Key Takeaways
| Concept | Description |
| Decorator | A function that modifies another function |
| Syntax | Use @decorator_name above the target function |
| Use Cases | Logging, validation, timing, caching, access control |
| Advanced | Arguments, nesting, functools.wraps, class methods |
| Caution | Don’t lose readability or metadata |
🚀 Final Thoughts
Decorators are one of the most elegant and powerful tools in Python. From simple wrappers to advanced meta-programming, they offer immense flexibility. If you’re a beginner, start using decorators in small scripts. If you’re an advanced user, consider how decorators can help enforce consistency, structure, and cleanliness across large projects.
Happy decorating! 🎨🐍