mediumBackend EngineerFintech
Explain Python decorators — how do they work, and when would you write a custom decorator?
Posted 18/04/2026
by Mehedy Hasan Ador
Question Details
At a fintech company:
"We need to add timing, logging, and access control to our API handlers without modifying each function. Can you implement decorators for these use cases?"
Suggested Solution
What Are Decorators?
Decorators are higher-order functions that wrap another function to extend its behavior.
def timer(func):
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start
print(f"{func.__name__} took {elapsed:.3f}s")
return result
return wrapper
@timer
def fetch_data(query):
# ... slow operation
return results
# Equivalent to: fetch_data = timer(fetch_data)
Decorators with Arguments
def require_role(role):
def decorator(func):
def wrapper(user, *args, **kwargs):
if user.role != role:
raise PermissionError(f"Requires {role}")
return func(user, *args, **kwargs)
return wrapper
return decorator
@require_role("admin")
def delete_user(user, user_id):
db.delete(user_id)
Class-Based Decorator (stateful)
class RateLimit:
def __init__(self, max_calls, period):
self.max_calls = max_calls
self.period = period
self.calls = []
def __call__(self, func):
def wrapper(*args, **kwargs):
now = time.time()
self.calls = [t for t in self.calls if now - t < self.period]
if len(self.calls) >= self.max_calls:
raise Exception("Rate limit exceeded")
self.calls.append(now)
return func(*args, **kwargs)
return wrapper
@RateLimit(max_calls=100, period=60)
def api_endpoint():
return {"status": "ok"}
functools.wraps (preserve metadata)
from functools import wraps
def logged(func):
@wraps(func) # Preserves __name__, __doc__, etc.
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
| Pattern | Use Case |
|---|---|
| Simple decorator | Logging, timing, caching |
| Decorator with args | Role-based access, config |
| Class decorator | Stateful (rate limiting, retry) |
@wraps | Always use to preserve function identity |