Introduction
Python decorators are powerful tools for modifying and enhancing function behavior. This tutorial explores the advanced technique of creating parameterized decorators, which allow developers to dynamically configure function wrappers with flexible arguments and complex transformation logic.
Decorator Basics
What is a Decorator?
In Python, a decorator is a powerful design pattern that allows you to modify or enhance functions and methods without directly changing their source code. Essentially, decorators are functions that take another function as an argument and return a modified version of that function.
Basic Decorator Syntax
def simple_decorator(func):
def wrapper():
print("Something before the function is called.")
func()
print("Something after the function is called.")
return wrapper
@simple_decorator
def say_hello():
print("Hello, LabEx!")
say_hello()
Key Characteristics of Decorators
| Characteristic | Description |
|---|---|
| Function Modification | Allows dynamic modification of function behavior |
| Non-invasive | Doesn't change the original function's source code |
| Reusability | Can be applied to multiple functions |
How Decorators Work
graph LR
A[Original Function] --> B[Decorator Function]
B --> C[Wrapped Function]
C --> D[Enhanced Functionality]
Types of Decorators
- Function Decorators: Modify individual functions
- Class Decorators: Modify entire classes
- Method Decorators: Modify class methods
Practical Example
def timer_decorator(func):
import time
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Function {func.__name__} took {end_time - start_time} seconds")
return result
return wrapper
@timer_decorator
def complex_calculation():
return sum(range(1000000))
complex_calculation()
Common Use Cases
- Logging
- Performance measurement
- Authentication
- Caching
- Input validation
Best Practices
- Keep decorators simple and focused
- Use
functools.wrapsto preserve original function metadata - Understand the performance implications of decorators
By understanding these basics, developers can leverage decorators to write more modular and efficient Python code with LabEx's recommended approach to clean, maintainable programming.
Parameterized Decorator Design
Understanding Parameterized Decorators
Parameterized decorators are advanced decorator functions that can accept arguments, allowing for more flexible and configurable function modifications.
Basic Structure of Parameterized Decorators
def decorator_with_args(arg1, arg2):
def actual_decorator(func):
def wrapper(*args, **kwargs):
## Decorator logic using arg1 and arg2
print(f"Decorator arguments: {arg1}, {arg2}")
return func(*args, **kwargs)
return wrapper
return actual_decorator
Decorator Argument Patterns
| Pattern | Description | Use Case |
|---|---|---|
| Configuration | Modify decorator behavior | Logging levels |
| Validation | Add input checks | Parameter constraints |
| Transformation | Modify function inputs/outputs | Data preprocessing |
Practical Implementation Example
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Hello, {name}!")
greet("LabEx")
Decorator Composition Flow
graph LR
A[Decorator Arguments] --> B[Decorator Factory]
B --> C[Actual Decorator]
C --> D[Wrapped Function]
D --> E[Enhanced Functionality]
Advanced Parameterized Decorator Techniques
Conditional Decoration
def conditional_decorator(condition):
def decorator(func):
def wrapper(*args, **kwargs):
if condition:
print("Condition met, applying decorator")
return func(*args, **kwargs)
return func(*args, **kwargs)
return wrapper
return decorator
@conditional_decorator(condition=True)
def example_function():
print("Function executed")
Common Use Cases
- Logging with configurable verbosity
- Caching with custom expiration
- Rate limiting
- Performance monitoring
- Input validation
Best Practices
- Keep decorator logic minimal
- Use
functools.wrapsto preserve function metadata - Ensure type safety and error handling
- Consider performance implications
Error Handling in Parameterized Decorators
def validate_type(expected_type):
def decorator(func):
def wrapper(*args, **kwargs):
for arg in args:
if not isinstance(arg, expected_type):
raise TypeError(f"Expected {expected_type}, got {type(arg)}")
return func(*args, **kwargs)
return wrapper
return decorator
@validate_type(int)
def add_numbers(a, b):
return a + b
By mastering parameterized decorators, developers can create more dynamic and flexible Python code with LabEx's advanced programming techniques.
Real-world Applications
Performance Monitoring Decorator
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__} execution time: {end_time - start_time} seconds")
return result
return wrapper
@performance_monitor
def complex_calculation(n):
return sum(range(n))
complex_calculation(1000000)
Authentication and Authorization Decorator
def require_auth(roles):
def decorator(func):
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if user.role in roles:
return func(user, *args, **kwargs)
raise PermissionError("Unauthorized access")
return wrapper
return decorator
class User:
def __init__(self, name, role):
self.name = name
self.role = role
@require_auth(['admin', 'manager'])
def delete_user(user, user_id):
print(f"User {user_id} deleted by {user.name}")
admin = User("Alice", "admin")
delete_user(admin, 123)
Caching Decorator with Expiration
from functools import wraps
import time
def cache_with_expiry(expiry_seconds):
def decorator(func):
cache = {}
def wrapper(*args, **kwargs):
key = str(args) + str(kwargs)
current_time = time.time()
if key in cache:
result, timestamp = cache[key]
if current_time - timestamp < expiry_seconds:
return result
result = func(*args, **kwargs)
cache[key] = (result, current_time)
return result
return wrapper
return decorator
@cache_with_expiry(expiry_seconds=5)
def expensive_computation(x, y):
time.sleep(2)
return x + y
Decorator Application Scenarios
| Scenario | Decorator Purpose | Key Benefits |
|---|---|---|
| Logging | Track function calls | Audit trails |
| Retry Mechanism | Handle transient failures | Resilience |
| Input Validation | Ensure data integrity | Error prevention |
| Rate Limiting | Control API usage | Resource management |
Retry Mechanism Decorator
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
@retry(max_attempts=3, delay=2)
def unreliable_network_call():
## Simulated network request
import random
if random.random() < 0.7:
raise ConnectionError("Network error")
return "Success"
Decorator Workflow
graph LR
A[Function Call] --> B{Decorator Check}
B --> |Pass| C[Original Function]
B --> |Fail| D[Error Handling]
C --> E[Return Result]
D --> F[Alternative Action]
Advanced Logging Decorator
import logging
import functools
def log_calls(log_level=logging.INFO):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
logging.log(log_level, f"Calling {func.__name__}")
try:
result = func(*args, **kwargs)
logging.log(log_level, f"{func.__name__} completed successfully")
return result
except Exception as e:
logging.error(f"{func.__name__} raised {type(e).__name__}: {e}")
raise
return wrapper
return decorator
@log_calls(log_level=logging.DEBUG)
def process_data(data):
## Data processing logic
pass
By exploring these real-world applications, developers can leverage decorators to create more robust, maintainable code with LabEx's advanced programming techniques.
Summary
By mastering parameterized decorators in Python, developers can create more modular, reusable, and adaptable code. These advanced techniques enable sophisticated function manipulation, providing elegant solutions for cross-cutting concerns like logging, authentication, and performance monitoring across different programming scenarios.



