Practical Implementation
Real-world Decorator Applications
1. Caching Method Results
def memoize(func):
cache = {}
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
class MathOperations:
@memoize
def fibonacci(self, n):
if n < 2:
return n
return self.fibonacci(n-1) + self.fibonacci(n-2)
import time
import functools
def performance_monitor(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
class DataProcessor:
@performance_monitor
def process_large_dataset(self, dataset):
return [x * 2 for x in dataset]
Decorator Implementation Patterns
Pattern |
Description |
Use Case |
Logging Decorator |
Tracks method calls |
Debugging, Auditing |
Validation Decorator |
Checks input parameters |
Data integrity |
Retry Decorator |
Handles transient failures |
Network operations |
Advanced Decorator Techniques
Parameterized Decorators
def retry(max_attempts=3, delay=1):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
attempts = 0
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
if attempts == max_attempts:
raise
time.sleep(delay)
return wrapper
return decorator
class NetworkService:
@retry(max_attempts=3, delay=2)
def fetch_data(self, url):
## Simulated network request
pass
Decorator Composition Flow
graph TD
A[Original Method] --> B[Decorator 1]
B --> C[Decorator 2]
C --> D[Decorator 3]
D --> E[Enhanced Method Behavior]
def validate_inputs(input_type):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for arg in args:
if not isinstance(arg, input_type):
raise TypeError(f"Expected {input_type}, got {type(arg)}")
return func(*args, **kwargs)
return wrapper
return decorator
class Calculator:
@validate_inputs(int)
def add_numbers(self, a, b):
return a + b
Context Management with Decorators
def context_manager(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("Entering context")
try:
result = func(*args, **kwargs)
print("Exiting context successfully")
return result
except Exception as e:
print(f"Error in context: {e}")
raise
return wrapper
class ResourceManager:
@context_manager
def process_resource(self, resource):
## Resource processing logic
pass
Best Practices for Decorator Implementation
- Use
functools.wraps
to preserve metadata
- Keep decorators focused and single-purpose
- Handle exceptions gracefully
- Consider performance implications
- Use type hints for clarity
Debugging and Testing Decorators
- Use
functools.wraps
to maintain function metadata
- Create unit tests for decorator logic
- Use introspection tools to examine decorated methods
By mastering these practical implementation techniques, LabEx users can create powerful, flexible, and maintainable Python code using decorators effectively.