Topic 9 of 64
Decorators
Overview
Decorators are a powerful Python feature that let you modify or enhance functions without changing their source code, using the @decorator syntax. They are used for authentication, caching, logging, timing, and validation — fundamental to web frameworks like Flask and Django.
Syntax
python
# Simple decorator
def log_calls(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@log_calls
def add(a, b):
return a + b
add(2, 3)
# Calling add
# add returned 5
# With arguments (decorator factory)
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_hello(): print("Hello!")Common Pitfalls
- Always use @functools.wraps(func) inside your wrapper — without it, the decorated function loses its __name__ and docstring.
- Decorators execute at definition time, not at call time — the function is wrapped when the module loads.
- Interview tip: Python's built-in @property, @staticmethod, @classmethod are all decorators.
Real-World Example
Authentication and caching decorators for a web API:
example
python
import functools
import time
# Timing decorator
def timer(func):
@functools.wraps(func) # preserve original function metadata
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.4f}s")
return result
return wrapper
# Cache decorator (memoization)
def cache(func):
memo = {}
@functools.wraps(func)
def wrapper(*args):
if args not in memo:
memo[args] = func(*args)
return memo[args]
return wrapper
# Authentication decorator for Flask-like routes
def require_auth(f):
@functools.wraps(f)
def decorated(*args, **kwargs):
token = request.headers.get("Authorization")
if not token or not validate_token(token):
return {"error": "Unauthorized"}, 401
return f(*args, **kwargs)
return decorated
@require_auth
@timer
def get_user_profile(user_id): ... # multiple decorators stack!