Decorators Reference
Wrapping functions to add behaviour — timing, logging, caching, access control.
Wrap a function to execute code before and/or after it runs.
Used In: Logging, timing, input validation, retry logic.
@decorator_name
def func(): ...def logger(func):
def wrapper(*args, **kwargs):
print(f'Calling {func.__name__}')
result = func(*args, **kwargs)
print(f'Done {func.__name__}')
return result
return wrapper
@logger
def process(n):
return n * 2
process(5)Calling process
Done processTime Complexity: O(1) wrapping overhead
Related Methods: functools.wraps()
Preserve the original function's __name__ and __doc__ inside a decorator.
Used In: All production-quality decorators.
from functools import wraps
@wraps(func)from functools import wraps
def logger(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@logger
def process(): pass
print(process.__name__)processTime Complexity: O(1)
Common Mistakes: Forgetting @wraps causes debugging tools to show the wrapper name instead of the original.
Create a decorator that accepts its own configuration parameters.
Used In: Retry logic, rate limiting, permission checks.
def decorator(param):
def outer(func):
def wrapper(*args, **kwargs): ...
return wrapper
return outerfrom functools import wraps
def retry(times=3):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(1, times + 1):
try:
return func(*args, **kwargs)
except Exception as e:
print(f'Attempt {attempt} failed: {e}')
raise RuntimeError('All retries failed')
return wrapper
return decorator
@retry(times=2)
def unstable_api():
raise ConnectionError('timeout')
try:
unstable_api()
except RuntimeError as e:
print(e)Attempt 1 failed: timeout
Attempt 2 failed: timeout
All retries failedTime Complexity: O(1) overhead
Related Methods: functools.wraps()
Use a class with __call__ as a decorator — useful for stateful decorators.
Used In: Call counting, circuit breakers, stateful rate limiters.
class Decorator:
def __call__(self, func): ...class CallCounter:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f'Call #{self.count}')
return self.func(*args, **kwargs)
@CallCounter
def greet(name):
return f'Hello, {name}'
greet('Alice')
greet('Bob')
print(greet.count)Call #1
Call #2
2Time Complexity: O(1) per call
Related Methods: __call__
Apply multiple decorators to one function — they execute bottom-up.
Used In: Chaining middleware: auth → rate-limit → log.
@dec_a
@dec_b
def func(): ...def bold(func):
def wrapper(): return '<b>' + func() + '</b>'
return wrapper
def italic(func):
def wrapper(): return '<i>' + func() + '</i>'
return wrapper
@bold
@italic
def text():
return 'hello'
print(text())<b><i>hello</i></b>Time Complexity: O(1)
@bold @italic means bold(italic(text)) — innermost decorator is applied first.
Python ships three built-in class decorators: @property, @staticmethod, @classmethod.
Used In: Controlled attribute access, computed properties, utility functions.
@property / @staticmethod / @classmethodclass Employee:
def __init__(self, salary):
self._salary = salary
@property
def salary(self):
return self._salary
@salary.setter
def salary(self, val):
if val < 0: raise ValueError('negative')
self._salary = val
@staticmethod
def currency():
return 'USD'
emp = Employee(90000)
print(emp.salary)
print(Employee.currency())90000
USDTime Complexity: O(1)
Related Methods: @classmethod, @abstractmethod