How to Implement the Decorator Pattern in Python to Add Behavior
This Python code demonstrates the decorator pattern by wrapping a function to add logging behavior without modifying the original function.
Python code
17 linesimport functools
def logger(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with {args} {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@logger
def add(a, b):
return a + b
if __name__ == "__main__":
print(add(3, 5))
Output
Calling add with (3, 5) {}
add returned 8
8
How it works
The decorator pattern allows you to extend the behavior of a function or class without permanently modifying it. Here, the logger decorator wraps the add function, so every call prints details about the arguments and the return value. The functools.wraps decorator is used to preserve the original function's metadata (like its name and docstring) on the wrapper. This pattern is a core part of Python, and many built-in decorators like @staticmethod and @property use the same underlying mechanism. Because the wrapper accepts *args and **kwargs, it can work with any function signature, making the decorator reusable across many functions.
Common mistakes
- Forgetting `@functools.wraps`, which loses the original function's metadata and breaks tooling like help().
- Not returning the wrapper function from the decorator, causing the decorated name to become `None`.
- Hardcoding argument names instead of using `*args` and `**kwargs`, which limits the decorator to only one signature.
Variations
- Use a class-based decorator that implements `__call__` to store state (e.g., call counts).
- Apply multiple decorators stacked on top of each other to add several behaviors at once.
Real-world use cases
- Adding timing or logging to every database query in a service without touching the query functions.
- Retrying network calls with exponential backoff by wrapping the request function in a resilient decorator.
- Enforcing authentication or permission checks on specific endpoint handlers in a web framework.
Sponsored
More from OOP & classes
- Add property getter setter validation in Python easy
- Binary Tree Inorder Traversal in Python easy
- Borg pattern shared state in Python medium
- Bridge Pattern in Python: Separate Abstraction from Implementation medium
- Composable Predicates with the &, |, ~ Operators in Python medium
- Composition over Inheritance: How to Build a Wallet Account in Python easy
Keep learning
Related tutorials and quizzes for this topic.