Decorators Intro
Decorators Intro
Section titled βDecorators IntroβWhat it means
Section titled βWhat it meansβA decorator is a function that wraps another function (or class) to add behavior β logging, timing, access control, caching β without changing the wrapped functionβs own code. The @decorator_name syntax above a function definition is shorthand for func = decorator_name(func).
Examples
Section titled βExamplesβdef logger(func): def wrapper(*args, **kwargs): print(f"Calling {func.__name__} with {args}, {kwargs}") result = func(*args, **kwargs) print(f"{func.__name__} returned {result}") return result return wrapper
@loggerdef add(a, b): return a + b
add(2, 3)# Calling add with (2, 3), {}# add returned 5# Without the @ shorthand, this is exactly what @logger doesdef add(a, b): return a + badd = logger(add)# Preserving the original function's metadata with functools.wrapsfrom functools import wraps
def logger(func): @wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper
@loggerdef add(a, b): """Add two numbers.""" return a + b
print(add.__name__) # 'add' β without @wraps this would print 'wrapper'# Decorators with their own arguments need an extra layerdef repeat(times): def decorator(func): def wrapper(*args, **kwargs): for _ in range(times): func(*args, **kwargs) return wrapper return decorator
@repeat(times=3)def greet(): print("Hi!")
greet() # prints "Hi!" three timesCommon mistake
Section titled βCommon mistakeβForgetting @wraps(func) β the wrapped function silently loses its __name__, __doc__, and signature, which breaks introspection, help text, and some testing/debugging tools.
def logger(func): def wrapper(*args, **kwargs): # missing @wraps(func) here return func(*args, **kwargs) return wrapper
@loggerdef add(a, b): """Add two numbers.""" return a + b
print(add.__name__) # 'wrapper' β wrong!print(add.__doc__) # None β the docstring is gone
# Fix: decorate the inner wrapper with functools.wraps(func)Quick practice
Section titled βQuick practiceβ-
What does
@loggerabove a function definition actually do under the hood?Answer
It replaces the function with `logger(func)` β equivalent to writing `func = logger(func)` right after the `def`. -
Why does a decoratorβs inner function usually accept
*args, **kwargs?Answer
So the wrapper works for *any* wrapped function's signature, not just one with a specific set of parameters. -
What problem does
functools.wrapssolve?Answer
It copies the original function's `__name__`, `__doc__`, and other metadata onto the wrapper, so decorated functions still introspect correctly.