There might be a situation where you'd want to pass an argument to a decorator.
Argument to Decorator Example
For example, you could extend the decorator you created before with a name argument that defines exactly who picked the items for you.
You could then use the decorator by passing different names to different functions at the time of wrapping them:
@decorator_func_with_argument("Zeek")
def prettify(msg):
print(msg)
@decorator_func_with_argument("Nature")
def feed(carbs, protein):
print(f"{carbs} and {protein}")
prettify("flowers for you")
# Zeek picked some flowers for you
feed("bread", "lentils")
# Nature picked some bread and lentils
Add Another Function Wrapper
To be able to handle arguments that you pass to your decorator, you need to go deeper to yet another level by adding another function wrapper to your decorator construct.
Instead of the conceptually descriptive but clunky name decorator_func_with_argument(), you'll name this decorator function add_message_from():
def add_message_from(name):
def decorator_func(initial_func):
def wrapper_func(*args):
print(f"{name} picked some")
return initial_func(*args)
return wrapper_func
return decorator_func
This construct might seem a bit intimidating at first, but you've already covered all the concepts that go into creating this beast. The only change you made was wrapping the decorator_func() from before with yet another function called add_message_from() that takes a parameter name.
Python Scope
Because of how scopes work, you're able to access the name variable in the inner scope of your wrapper_func() without explicitly needing to pass it. And because you added the outer layer add_message_from() and defined that it takes one positional argument, your decorator is now able to handle receiving an argument:
@add_message_from("Zeek")
def prettify(msg):
print(msg)
prettify("flowers for you") # Zeek picked some flowers for you
You can see that it's now necessary to use the parentheses (()) after the name of your decorator when wrapping a function so that you're able to pass in the required argument.
Tasks
- What happens when you try to wrap a function with your decorator without passing an argument to it?
- Try to run this code example and use the same
decorator_func()with different arguments to create differently altered functions. - Extend the decorator that repeats the output of wrapped functions in a way that you can pass in an argument that defines how often the output should be repeated.
Additional Resources
- Real Python: Primer on Python Decorators
- Jack Diederich at PyCon 2019: Class Decorators: Radically Simple
Summary: Python Decorators With an Argument
- Decorators can take arguments by wrapping the decorator definition into another function
- The additional function on the decorator definition will handle the arguments
- The function syntax with parentheses is required for passing the argument while decorating the function