Decorators are one of Python's most powerful features, allowing you to modify the behavior of a function or class transparently. At their core, decorators are just higher-order functions: they take a function as input and return a new function.
1. The Closure Pattern
A standard decorator uses a closure—a nested function that captures the outer scope (the original function) and replaces it.
2. Preserving Metadata with functools.wraps
When you wrap a function, the new function (the wrapper) replaces the original. This means you lose the original function's __name__ and __doc__. To fix this, always use @functools.wraps.
3. Decorators with Arguments
If you want to pass arguments to the decorator itself (like @repeat(times=3)), you need three levels of functions. The outermost function accepts the arguments and returns the decorator.
4. Stacking Decorators
You can apply multiple decorators to a single function. They are applied from the bottom up (closest to the function definition is applied first).