Python Decorators
Write decorators, use @functools.wraps, pass arguments to decorators, with examples.
What is a Decorator?
A decorator is a function that wraps another function to add behaviour before or
after it runs, without modifying the original code. It's used for logging, timing, authentication,
and caching — cross-cutting concerns you want to apply consistently. The @name syntax
is just clean shorthand for func = decorator(func).
def log_call(func): def wrapper(*args, **kwargs): print(f"→ Calling {func.__name__}") result = func(*args, **kwargs) print(f"← Finished") return result return wrapper @log_call def add(a, b): return a + b print(add(3, 7))
→ Calling add ← Finished 10
Timing Decorator with @functools.wraps
Without @functools.wraps(func), the wrapped function loses its original name and
docstring. Always include it — it preserves the original function's metadata so tools and introspection
still work correctly.
import time, functools def timer(func): @functools.wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) print(f"{func.__name__} took {time.perf_counter()-start:.4f}s") return result return wrapper @timer def big_sum(): return sum(range(1_000_000)) big_sum()
big_sum took 0.0301s
Decorator with Arguments
To pass arguments to a decorator, add a third level of nesting: the outer function takes the argument, returns a decorator, which wraps the function.
def repeat(n): def decorator(func): def wrapper(*a, **kw): for _ in range(n): func(*a, **kw) return wrapper return decorator @repeat(3) def say_hi(): print("Hi!") say_hi()
Hi! Hi! Hi!
@property, @classmethod, @staticmethod, and @functools.lru_cache.🧠 Quick Check
What does @functools.wraps(func) preserve?