Changing behavior without changing the original function body
A decorator receives a function or class and returns a wrapped or modified version. The @decorator syntax applies that transformation at definition time.
Common real-world uses
Frameworks use decorators for routing and authorization. Applications can use them for logging, timing, caching, retries and validation. The wrapper should preserve the original function's important behavior.
Use functools.wraps
functools.wraps preserves metadata such as the original function name and documentation. Without it, debugging and introspection can become confusing.
Practice: create a decorator that measures execution time and reports the wrapped function's name and duration.