Python tutorials > Advanced Python Concepts > Decorators > How to chain decorators?
How to chain decorators?
Chaining decorators in Python involves applying multiple decorators to a single function. This allows you to compose different functionalities and apply them in a sequential manner. Each decorator modifies the behavior of the function in a specific way, and the order in which they are applied matters significantly.
Basic Chaining Example
This example demonstrates how to chain two simple decorators, The output will be:decorator1
and decorator2
, to the say_hello
function. The decorators are applied from bottom to top. Therefore, decorator2
is applied first, and then decorator1
is applied to the result of decorator2
.Decorator 1: Before function execution
Decorator 2: Before function execution
Hello, World!
Decorator 2: After function execution
Decorator 1: After function execution
def decorator1(func):
def wrapper(*args, **kwargs):
print('Decorator 1: Before function execution')
result = func(*args, **kwargs)
print('Decorator 1: After function execution')
return result
return wrapper
def decorator2(func):
def wrapper(*args, **kwargs):
print('Decorator 2: Before function execution')
result = func(*args, **kwargs)
print('Decorator 2: After function execution')
return result
return wrapper
@decorator1
@decorator2
def say_hello(name):
print(f'Hello, {name}!')
say_hello('World')
Concepts Behind the Snippet
Decorator chaining relies on the fact that decorators are functions that take a function as input and return a modified function. When multiple decorators are applied using the The expression: is equivalent to:@
syntax, they are effectively nested.@decorator1
@decorator2
def my_function():
pass
my_function = decorator1(decorator2(my_function))
Real-Life Use Case Section
Decorator chaining is commonly used in web frameworks for tasks like authentication, authorization, and logging. For example, you might have one decorator that checks if a user is authenticated and another that checks if the user has the necessary permissions to access a resource. You can then chain these decorators to ensure both conditions are met before allowing access. Another use case includes data validation. You might chain decorators to validate input data according to different rules.
Best Practices
Interview Tip
When discussing decorator chaining in an interview, be prepared to explain: Also, be ready to write a simple example of chained decorators on the whiteboard.
When to use them
Use decorator chaining when you need to apply multiple aspects to a function without cluttering the core logic. This includes applying multiple validation checks, pre- and post-processing steps, or access control layers. It promotes a cleaner, more modular design where concerns are clearly separated.
Memory footprint
Decorator chaining doesn't significantly increase the memory footprint, especially when implemented correctly. Each decorator essentially wraps the original function with additional logic, but the underlying function and its wrapped versions are still stored only once in memory. However, if the decorators themselves perform memory-intensive operations (e.g., caching large datasets), the memory usage will increase regardless of whether they are chained.
Alternatives
While decorator chaining provides a concise way to apply multiple functionalities, alternatives include:
@
syntax, you could manually wrap a function with multiple decorator calls (e.g., my_function = decorator1(decorator2(my_function))
). This approach can be less readable, especially with more decorators.
Pros
@
syntax makes it clear which decorators are being applied to a function.
Cons
FAQ
-
What happens if two decorators modify the same arguments?
The behavior depends on the order of the decorators and how they modify the arguments. The decorator applied last (i.e., the one closest to the function definition) will have the final say on the arguments passed to the wrapped function.
-
Can I chain decorators with arguments?
Yes, you can chain decorators that take arguments. You define decorator factories that return the actual decorator functions.
Example:
def repeat(num_times): def decorator_repeat(func): def wrapper(*args, **kwargs): for _ in range(num_times): result = func(*args, **kwargs) return result return wrapper return decorator_repeat @repeat(num_times=3) def greet(name): print(f'Hello, {name}!') greet('Alice')
-
Is there a limit to how many decorators I can chain?
There's no hard limit imposed by Python, but chaining too many decorators can make your code harder to read and debug. It's generally best to keep the number of chained decorators to a reasonable level (e.g., 2-3).