Python tutorials > Advanced Python Concepts > Decorators > How to use decorators with arguments?
How to use decorators with arguments?
Basic Decorator Structure
my_decorator
function takes the function to be decorated as an argument and returns a wrapper function. The wrapper function executes code before and after the original function.
def my_decorator(func):
def wrapper(*args, **kwargs):
# Do something before
result = func(*args, **kwargs)
# Do something after
return result
return wrapper
@my_decorator
def say_hello(name):
return f"Hello, {name}!"
print(say_hello("Alice"))
Creating a Decorator with Arguments
repeat
is the outer function that takes num_times
as an argument. It returns decorator_repeat
, which is the actual decorator. The decorator_repeat
function then takes the function to be decorated (func
) as an argument and returns the wrapper function.
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("Bob")
Explanation of the Code
repeat
function is called with num_times=3
, which returns the decorator_repeat
function. Then, the decorator_repeat
function is applied to the greet
function. The wrapper
function inside decorator_repeat
executes the greet
function multiple times based on the num_times
argument. This approach allows you to create flexible decorators that can be customized using arguments.
Concepts Behind the Snippet
repeat
function returns another function (decorator_repeat
), which 'closes over' the num_times
argument. This means that decorator_repeat
remembers the value of num_times
even after repeat
has finished executing. The decorator_repeat
function then returns another function (wrapper
) which closes over the original function func
. This pattern enables the creation of decorators that can be parameterized.
Real-Life Use Case Section
Best Practices
functools.wraps
: When creating decorators, use functools.wraps
to preserve the original function's metadata (name, docstring, etc.). This improves introspection and debugging.
Using functools.wraps
functools.wraps
copies the metadata from the decorated function to the wrapper function. Without it, greet.__name__
would be wrapper
and greet.__doc__
would be None
. This is important for debugging and understanding your code.
import functools
def repeat(num_times):
def decorator_repeat(func):
@functools.wraps(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):
"""Greets someone."""
print(f"Hello, {name}!")
print(greet.__name__)
print(greet.__doc__)
Interview Tip
functools.wraps
is crucial.
When to use them
Memory Footprint
Alternatives
Pros
Cons
functools.wraps
, introspection can be difficult.
FAQ
-
Why use
functools.wraps
?
functools.wraps
preserves the original function's metadata (name, docstring, etc.), making debugging and introspection easier. Without it, the decorated function would lose its original identity. -
Can I use decorators with arguments on class methods?
Yes, decorators with arguments can be used on class methods. The first argument passed to the method will beself
. -
How do I handle exceptions in decorators?
You can handle exceptions within the wrapper function. You can either catch and log the exception or re-raise it after performing some cleanup or logging.