Practical Use Cases of Decorators
Caching
Decorators can be used to implement caching, which can improve the performance of your application by reducing the number of expensive computations.
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n <= 1:
return n
else:
return (fibonacci(n-1) + fibonacci(n-2))
print(fibonacci(100)) ## Output: 354224848179261915075
In this example, the lru_cache
decorator from the functools
module is used to cache the results of the fibonacci
function, which can be computationally expensive for large input values.
Logging and Debugging
Decorators can be used to add logging and debugging functionality to your functions, without modifying the core functionality of the functions.
def log_call(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args={args} and kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"Function {func.__name__} returned {result}")
return result
return wrapper
@log_call
def add(a, b):
return a + b
result = add(2, 3)
## Output:
## Calling add with args=(2, 3) and kwargs={}
## Function add returned 5
In this example, the log_call
decorator logs the function call and the return value, without modifying the add
function itself.
Authentication and Authorization
Decorators can be used to implement authentication and authorization checks in your application, ensuring that only authorized users can access certain functionality.
def requires_admin(func):
def wrapper(*args, **kwargs):
if not is_admin(current_user):
raise PermissionError("Only admins can access this function")
return func(*args, **kwargs)
return wrapper
@requires_admin
def delete_user(user_id):
## delete user logic
pass
In this example, the requires_admin
decorator checks if the current user is an admin before allowing them to access the delete_user
function.
Error Handling
Decorators can be used to add error handling and exception management to your functions, without modifying the core functionality of the functions.
def handle_exceptions(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"Error occurred in {func.__name__}: {e}")
raise e
return wrapper
@handle_exceptions
def divide(a, b):
return a / b
result = divide(10, 0) ## Output: Error occurred in divide: division by zero
In this example, the handle_exceptions
decorator wraps the divide
function and catches any exceptions that may be raised, logging the error and re-raising the exception.
These are just a few examples of the practical use cases for decorators in Python. Decorators can be used to add a wide range of functionality to your functions, from caching and logging to authentication and error handling.