Decorators Basics (Function Wrapping)
On this page
Decorators Add Policy
Decorators wrap functions to add behavior consistently. In production, they are useful for policy enforcement (timeouts, retries, logging) if kept transparent.
Simple Timing Decorator
import time
from functools import wraps
def timed(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
start = time.perf_counter()
try:
return fn(*args, **kwargs)
finally:
elapsed_ms = (time.perf_counter() - start) * 1000
print(f"event=timed fn={fn.__name__} elapsed_ms={elapsed_ms:.2f}")
return wrapper
Usage
@timed
def work():
return "ok"
Operational Checklist
- Always use
@wrapsto preserve name and docstring. - Keep decorator behavior deterministic; avoid hidden network calls.
- Make side effects (logging, retries) obvious to callers.
Failure Modes
- Metadata loss: without
@wraps, debugging and tracing suffer. - Hidden behavior: decorators stack and become hard to reason about.