In Python, a decorator is a function that takes another function as an argument and returns a new function that generally extends the behavior of the original.
Decorators allow modifying or extending the behavior of functions or methods without changing their source code. They are widely used for cross-cutting concerns such as authentication, logging, and validation, among others.
Defining a Decorator
Let’s see it with an example
def my_decorator(func):
def wrapper():
print("Something is done before the function")
func()
print("Something is done after the function")
return wrapper
In this example:
my_decorator
is a function that takes a functionfunc
- It defines a function
wrapper
(it could have any name) - The
wrapper
function uses the receivedfunc
function - Finally,
my_decorator
returns thewrapper
function
Using a Decorator
Now, to use the decorator the @
symbol followed by the decorator’s name is used, before the definition of the function to be decorated.
@my_decorator
def greet():
print("Hello from LuisLlamas.es!")
greet()
# Output:
# Something is done before the function
# Hello from LuisLlamas.es!
# Something is done after the function
In this example:
- When
greet
is called,my_decorator
actually “takes control” my_decorator
receives thegreet
function and returns thewrapper
function that uses it- This adds behavior before and after the execution of
greet
Decorators with Arguments
Decorators can also accept arguments. To do this, functions need to be nested:
- An outer function to accept the decorator’s arguments
- An inner function that accepts the function to be decorated.
For example,
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_goodbye():
print("Goodbye!")
say_goodbye()
# Output:
# Goodbye!
# Goodbye!
# Goodbye!
In this case,
repeat
is a decorator that accepts an argumentn
- This repeats the execution of the decorated function
n
times
Multi-level Decorators
Python allows nesting decorators, applying multiple decorators to the same function.
def decorator1(func):
def wrapper():
print("Decorator 1 before")
func()
print("Decorator 1 after")
return wrapper
def decorator2(func):
def wrapper():
print("Decorator 2 before")
func()
print("Decorator 2 after")
return wrapper
@decorator1
@decorator2
def function():
print("Function is running")
function()
# Output:
# Decorator 1 before
# Decorator 2 before
# Function is running
# Decorator 2 after
# Decorator 1 after
In this example, function
is first decorated by decorator2
and then by decorator1
, showing how multiple decorators can be chained.