decorators
Published 2025-05-13
A decorator is a function that takes another function as its argument and returns a new function that can incorporate the original function’s behavior and add functionality to it.
Timing decorator example:
def timing_decorator(f):
@functools.wraps(f) def wrapper(*args, **kwargs): start = time.perf_counter() result = f(*args, **kwargs) print(f"Function {f.__name__} took {time.perf_counter() - start} seconds to execute.") return result
return wrapper
@timing_decoratordef quick_function(n): """A sample function that does a quick computation.""" result = sum(i*i for i in range(n)) return f"Sum of squares up to {n-1} is {result}"The decorator syntax:
@timing_decoratordef f(): passis equivalent to:
f = timing_decorator(f)timing_decorator returns the wrapper over the inputted function, which is closed over1 1. See the closures snippet. ↩ by the wrapper. Positional and keyword arguments to the function are passed to the wrapper, which can return the function’s output, a modified output, or something else altogether (such as wall time).
@functools.wraps(f) copies the metadata of f to the function it’s decorating. It’s used here because the metadata of f, including __name__ and __doc__, gets overridden by wrapper. Adding @functools.wraps(f) to the wrapper function therefore preserves f’s metadata.
print(f"Name of quick_function: {quick_function.__name__}")# Output: Name of quick_function: quick_functionprint(f"Docstring of quick_function: {quick_function.__doc__}")# Output: Docstring of quick_function: A sample function that does a quick computation.