Decorator chaining is the process of applying multiple decorators to a single function. Each decorator wraps the function and adds its own functionality, allowing multiple behaviors to be combined without modifying the original code. Following sections explains how decorator chaining works, the order of execution and how multiple decorators interact with one another.
Example: Chaining Multiple Decorators
def decorator1(func):
def wrapper():
print("Decorator 1 - Before Function")
func()
print("Decorator 1 - After Function")
return wrapper
def decorator2(func):
def wrapper():
print("Decorator 2 - Before Function")
func()
print("Decorator 2 - After Function")
return wrapper
@decorator1
@decorator2
def greet():
print("Hello, World!")
greet()
Output
Decorator 1 - Before Function Decorator 2 - Before Function Hello, World! Decorator 2 - After Function Decorator 1 - After Function
Decorator
A decorator is a function that can take a function as an argument and extend its functionality and return a modified function with extended functionality.

Order of Execution in Decorator Chaining
When multiple decorators are used, Python applies them from the bottom up. The decorator closest to the function is applied first, and the resulting function is then passed to the decorator above it. This order determines how the decorators interact and affect the final behavior of the function.
Example:
@decor1
@decor2
def num():
return 10
is equivalent to:
num = decor1(decor2(num))
Here, decor2 wraps the original num() function first, and the function returned by decor2 is then wrapped by decor1.
Explanation:
- Apply Decorators Bottom-Up: The decorator nearest to the function is applied first.
- Wrap Functions Sequentially: Each decorator receives and wraps the function returned by the previous decorator.
- Determine Execution Order: The order of decorators affects how the function is processed and what output is produced.
- Combine Multiple Behaviors: Each decorator contributes its own functionality to the final wrapped function.
Syntax of decorator:
@decor1
@decor2
def num():
pass
Example 1: Two decorators are applied to num() to demonstrate decorator chaining. Python applies decorators from the bottom up, so @decor executes first and its result is then passed to @decor1 for further processing.
# code for testing decorator chaining
def decor1(func):
def inner():
x = func()
return x * x
return inner
def decor(func):
def inner():
x = func()
return 2 * x
return inner
@decor1
@decor
def num():
return 10
print(num())
Output
400
Explanation:
- decor1 takes a function as input and returns a wrapper function that squares the value returned by the original function.
- decor takes a function as input and returns a wrapper function that doubles the value returned by the original function.
- Both decorators are applied to num() using the @ syntax.
- Python interprets the decorators as num = decor1(decor(num)), so decor is applied first and decor1 is applied afterward.
- When num() is called, it initially returns 10.
Example 2: Multiple decorators are applied to sayhellogfg() to show how decorator chaining works. Each decorator contributes its own behavior, resulting in a combined effect when the function is executed.
def decor1(func):
def wrap():
print("************")
func()
print("************")
return wrap
def decor2(func):
def wrap():
print("@@@@@@@@@@@@")
func()
print("@@@@@@@@@@@@")
return wrap
@decor1
@decor2
def sayhellogfg():
print("Hello")
def saygfg():
print("GeekforGeeks")
sayhellogfg()
saygfg()
Output
************ @@@@@@@@@@@@ Hello @@@@@@@@@@@@ ************ GeekforGeeks
Explanation:
- decor1 and decor2 define wrapper functions that print messages before and after executing the original function.
- Both decorators are applied to sayhellogfg() using the @ syntax.
- Python interprets the decorators as sayhellogfg = decor1(decor2(sayhellogfg)).
- When sayhellogfg() is called, decor1 executes first, followed by decor2, and then the original function.
Decorators With Arguments
Decorators can also accept arguments, allowing their behavior to be customized when applied to a function. These decorators return another decorator function, which then wraps the target function. Multiple parameterized decorators can be chained in the same way as regular decorators.
Example:
def repeat(times):
def decorator(func):
def wrapper():
for _ in range(times):
func()
return wrapper
return decorator
def message(text):
def decorator(func):
def wrapper():
print(text)
func()
return wrapper
return decorator
@message("Welcome")
@repeat(2)
def greet():
print("Hello")
greet()
Output
Welcome Hello Hello
Explanation:
- Accept Parameters: repeat() and message() receive arguments when the decorators are applied.
- Return Decorators: Each function returns a decorator that wraps the target function.
- Chain Parameterized Decorators: Multiple decorators with arguments can be stacked using the @ syntax.
- Apply in Order: Python first applies @repeat(2) and then wraps the result with @message("Welcome").
- Customize Function Behavior: Decorator arguments make decorators more flexible and reusable.