Functions that wrap functions — the pattern behind routes, caching, and retries
Decorators appear everywhere in real Python: Flask/FastAPI routes (@app.get), pytest fixtures (@pytest.fixture), caching (@lru_cache), retry logic, access control, and timing. You need to know how to write one, not just recognise the @ symbol.
Understanding decorators also unlocks a mental model: functions are objects, and returning a function from a function is completely normal. This is the same thinking needed for closures and higher-order design patterns.
A decorator is a function that takes a function and returns a (usually enhanced) function. The @decorator syntax is pure sugar — @log_calls above def process(...) is identical to writing process = log_calls(process) immediately after the function definition.
The inner wrapper function is what gets called instead of the original. It can run code before and after the original, modify arguments or return values, or skip the original entirely.
A decorator is a function that takes a function and returns a replacement — adding behaviour before or after the original runs. The @name syntax is shorthand for func = decorator(func).
Always use @functools.wraps(func) on the wrapper. Without it, the decorated function loses its __name__, __doc__, and signature — breaking introspection and debugging tools.
When the decorator itself needs configuration (e.g. retry count, exception types), add one extra nesting level — the outermost function takes the config and returns the actual decorator.
A decorator with arguments needs one extra nesting level. Three layers: outer function accepts config → returns the decorator → decorator wraps the function.
@cache memoizes a pure function — repeated calls with the same arguments return the cached result instantly instead of recomputing. Stacking multiple decorators applies them from innermost to outermost.
@cache memoizes pure functions — identical arguments return the cached result. Requires hashable arguments. When stacking, the decorator closest to the function applies first.