Python - Decorators
Overview
Estimated time: 30–40 minutes
Decorators wrap callables to add behavior (logging, caching, validation). Learn how they work and how to build reliable decorators.
Learning Objectives
- Explain how decorators are just callables taking and returning callables.
- Write decorators that preserve metadata with
functools.wraps. - Use common patterns: memoization (
lru_cache), retry wrappers, access checks.
Prerequisites
- Functions and closures
Examples
from functools import wraps
def shout(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
result = fn(*args, **kwargs)
return str(result).upper()
return wrapper
@shout
def greet(name):
return f"Hello, {name}!"
print(greet("Ada"))
Expected Output:
HELLO, ADA!
Guidance & Patterns
- Explain that
@decoratoris syntax sugar forfn = decorator(fn). - Demonstrate parameterized decorators (
@decorator(arg)) that return a decorator. - Show how missing
@wrapsbreaks__name__/__doc__and tooling.
Best Practices
- Prefer composition over complex, stateful decorators. Keep wrappers thin and testable.
- For caching, prefer
functools.lru_cacheover homegrown caches; document invalidation strategy. - Mind async functions: use
async defwrappers and consideranyiofor structured concurrency.
Exercises
- Implement a
@retry(n=3)decorator with exponential backoff for exceptions. - Write a decorator that validates function arguments using type hints at runtime.