
Advanced Python Decorators for Code Reusability and Maintainability
Decorators 101
Decorators are implemented using the @decorator syntax and are essentially functions that wrap another function to extend its behavior. A basic decorator might look like this:
def simple_decorator(func):
def wrapper(*args, **kwargs):
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper
@simple_decorator
def greet(name):
print(f"Hello, {name}!")When greet("Alice") is called, the simple_decorator executes the wrapper function, which adds behavior before and after the original function.
Class-Based Decorators
While function-based decorators are common, class-based decorators can be more flexible, especially when you need to maintain state between calls.
class Counter:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"Function {self.func.__name__} has been called {self.count} times.")
return self.func(*args, **kwargs)
@Counter
def add(a, b):
return a + b
add(2, 3)
add(4, 5)Each call to add increments the counter and prints the result. This approach is useful for metrics, logging, or rate limiting.
Stacking and Chaining Decorators
Multiple decorators can be applied to a single function in a specific order. The order matters as the execution is from the innermost to the outermost.
def decorator_one(func):
def wrapper(*args, **kwargs):
print("Decorator One")
return func(*args, **kwargs)
return wrapper
def decorator_two(func):
def wrapper(*args, **kwargs):
print("Decorator Two")
return func(*args, **kwargs)
return wrapper
@decorator_one
@decorator_two
def say_hello():
print("Hello!")
say_hello()In this example, the output will be:
Decorator One
Decorator Two
Hello!Decorators with Arguments
Sometimes, you may need to pass arguments to a decorator itself. This involves an additional level of nesting.
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def print_message(msg):
print(msg)
print_message("Hello, decorator!")This decorator repeats the function call three times. The structure of repeat is a factory function that returns the actual decorator.
Decorators for Caching (Memoization)
Memoization is a technique to cache the results of expensive function calls. Python provides functools.lru_cache for this purpose, but implementing a custom cache can be useful for learning or specific scenarios.
from functools import wraps
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@memoize
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10)) # Efficient computation due to memoizationThis memoize decorator stores computed results to avoid redundant function calls.
Decorators for Access Control
Decorators can enforce access control based on user permissions or roles.
def check_permission(roles):
def decorator(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if user.role in roles:
return func(user, *args, **kwargs)
raise PermissionError("User does not have required permissions.")
return wrapper
return decorator
class User:
def __init__(self, name, role):
self.name = name
self.role = role
@check_permission(['admin'])
def delete_record(user):
print(f"Record deleted by {user.name} ({user.role})")
admin_user = User("Alice", "admin")
delete_record(admin_user) # Succeeds
non_admin_user = User("Bob", "user")
delete_record(non_admin_user) # Raises PermissionErrorThis pattern is useful for securing APIs or enforcing business rules in applications.
Comparing Decorator Approaches
| Type | Use Case | Maintainability | Flexibility | Performance |
|---|---|---|---|---|
| Function Decorator | Simple behavior extension | High | Medium | Good |
| Class Decorator | Stateful behavior | Medium | High | Medium |
| Stacked Decorators | Layered behavior | High | High | Good |
| Arg-accepting Decorators | Configurable behavior | Medium | High | Medium |
| Caching Decorators | Optimization | High | Medium | Excellent |
