Reading — step 1 of 7
Learn
You've built a web app with fifty endpoint functions, and your boss says: every endpoint needs to log who called it AND how long it took. Adding 5 lines to each of 50 functions = 250 lines of repeated code, plus a guarantee that the next person writing endpoint #51 forgets to add it.
Decorators solve this. A decorator is a function that takes another function, wraps it with additional behavior, and returns the enhanced version — without touching the original function's own code. Built on closures (function returning a function) plus a tiny bit of syntax sugar.
A decorator from scratch
What the @shout line does:
The @ is just syntactic sugar for "replace greet with shout(greet)." After that line, the name greet points at wrapper (which calls the original inside).
Anatomy of a typical decorator
*args, **kwargs make the wrapper work for any function signature.
A useful real example: timing
functools.wraps — preserve metadata
Without @wraps(func), the wrapped function loses its name, docstring, and module:
Adding @wraps(func) to your wrapper definition copies the metadata from the original to the wrapper. Now greet.__name__ is "greet" and the docstring is preserved. Always use functools.wraps.
Stacking decorators
Reads BOTTOM-UP: greet = timer(shout(greet)). The closest decorator to the function is applied first.
Decorators with arguments
For decorators that take arguments themselves, you need ONE more level of nesting:
The call repeat(3) returns the actual decorator, which then wraps hi.
Built-in decorators you've seen
@staticmethod,@classmethod(in classes)@property— turns a method into a read-only attribute@functools.lru_cache— automatic memoization (caching) of return values@dataclasses.dataclass— auto-generates__init__,__repr__,__eq__
Common mistakes
- Forgetting
return result— the wrapped function silently returns None. - Forgetting
@wraps(func)— debugging tools andhelp()show the wrong info. - Decorator argument confusion —
@timer(no args) needs ONE level of nesting;@retry(3)(with args) needs TWO levels. - Mutating shared state in the decorator without locks — fine for single-threaded code, dangerous in async/threaded.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…