Practical Decorator Patterns
Timing Decorator
import time
import functools
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} executed in {end_time - start_time:.4f} seconds")
return result
return wrapper
@timer
def slow_function():
time.sleep(2)
Caching Decorator
def memoize(func):
cache = {}
@functools.wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
Authentication Decorator
def authenticate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
user = kwargs.get('user')
if not user or not user.is_authenticated:
raise PermissionError("Authentication required")
return func(*args, **kwargs)
return wrapper
class User:
def __init__(self, is_authenticated=False):
self.is_authenticated = is_authenticated
@authenticate
def sensitive_operation(user):
print("Performing sensitive operation")
Decorator Patterns Comparison
Pattern |
Purpose |
Key Characteristics |
Timing |
Performance Measurement |
Tracks execution time |
Caching |
Performance Optimization |
Stores and reuses results |
Authentication |
Access Control |
Validates user permissions |
Logging |
Debugging |
Captures function call details |
Decorator Composition
def decorator1(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("Decorator 1 before")
result = func(*args, **kwargs)
print("Decorator 1 after")
return result
return wrapper
def decorator2(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("Decorator 2 before")
result = func(*args, **kwargs)
print("Decorator 2 after")
return result
return wrapper
@decorator1
@decorator2
def combined_example():
print("Original function")
Decorator Execution Flow
graph TD
A[Original Function] --> B[Decorator 1]
B --> C[Decorator 2]
C --> D[Function Execution]
D --> E[Decorator 2 Post-processing]
E --> F[Decorator 1 Post-processing]
Advanced Decorator Techniques
- Parameterized Decorators
- Class Method Decorators
- Decorator Factories
- Preserving Metadata with
functools.wraps
- Minimal overhead for simple decorators
- Caching can significantly improve performance
- Be cautious with complex decorator logic
- Use
functools
for metadata preservation
LabEx Practical Tip
When learning decorators, start with simple patterns and gradually explore more complex use cases. LabEx recommends practicing each pattern to build a solid understanding.