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.