Decorating Functions With Arguments
This lesson will show you how to use the *args
and **kwargs
syntax to modify decorators to accept any number of arguments.
00:00
What if you need to pass arguments into your decorated functions? Let me show you that next. I’ll have you keep using decorators.py
. Back here in the REPL, from your module decorators
, import do_twice
. To decorate a new function, start with @do_twice
, then define like normal.
00:27
But this time, let’s say we’re defining greet()
with the argument name
, and then it will use an f-string, with the curly braces, to print that name
.
00:40
So here’s greet
. It looks like it’s been properly decorated. In this case, you pass in "World"
, so it will say "Hello World"
—
00:52
uh oh, it looks like we found an error. The wrapper_do_twice()
function that’s part of the decorator that you’ve imported takes zero positional arguments, and you gave it one.
01:04
Could you modify your decorator @do_twice
so that it could take an argument? Sure. This may get a little messy though. So back inside decorators.py
, you could create an argument named one_arg
and pass it into the function—in this case, both function calls—and save. In order for you to reuse this decorator,
01:35 you’ll need to exit the REPL and then re-import it.
01:43 Let’s start my REPL again.
01:54
And redefine your function, greet()
, printing f"Hello {name}"
. Okay, so here’s greet
. Pass into greet()
, "World"
when you call it—great! That worked fine.
02:14
What about if you create using the same decorator @do_twice
your old friend the function say_whee()
? Oh, don’t forget the exclamation point.
02:28
Okay. So here’s say_whee
—looks good. Let me have you call it. So now it’s saying it’s missing one required positional argument, but in the case of greet()
—
02:46
give it an argument—that works fine. I’m going to go back in time just one moment and show you something. When calling say_whee()
in my REPL bpython
, it shows that say_whee()
takes one argument, which isn’t true. When it was defined, it accepted no arguments.
03:10 That’s very interesting. That’s something the decorator is doing. And since this decorator is set up to take one argument—that’s the name of the argument—it’s applying that to this function also.
03:25 Is it possible back here in our decorator to be flexible? To accept one, two, three—multiple arguments, and also potentially except none? Sure! There’s a standardized way to do that. Let’s rewrite this.
03:46
You’re going to add *args
and **kwargs
—which stands for arguments and keyword arguments—to your decorator.
04:00 And whatever arguments or keyword arguments that are passed through our wrapper here will get passed when those functions are called. So, let’s save. Okay. So the trick again, back here in our REPL, is I do need to exit and start my REPL again.
04:22
Okay. Now, if you import, from decorators import do_twice
, can you define @do_twice
decorating greet()
?
04:36
Here’s greet
. It looks good. Notice how, as I type this, it shows greet
with *args
and **kwargs
available.
04:46
Let’s greet the world. Great! That worked fine. And what if you apply @do_twice
—the decorator—to a simpler function, like say_whee()
, that takes no arguments—will that work?
05:03
So, here it is. Yep! It looks good. And again, if I freeze for a second, you’ll see that say_whee()
, which by definition didn’t take any arguments, shows *args
and **kwargs
. Not that that function, which the way it was defined, can actually accept any. The decorator would accept arguments, and then pass them into our function, and that’s where it would fail.
05:32
And now call it without any arguments. So now, functions that have no arguments can be decorated with @do_twice
, and functions that do have arguments passed in can be decorated with that same decorator, because you have added *args
and **kwargs
and are passing those into our functions.
05:59 That’s the flexibility of using them. Great! Next, let’s return values from decorated functions.
Chris Bailey RP Team on May 8, 2019
Hi Vanam, You are correct. If you don’t think your wrapped functions will do anything with keyword arguments, then you could use (*args) only. The method shown will allow either type, and will not have issues if you only use arguments and not keyword arguments (or vice versa). It would, as a tool, be more flexible.
grzegorzmalinowski on Jan. 8, 2020
Is it possibly inside the wrapper to pass new arg vaalue to the function to be decorated?
Chris Bailey RP Team on Jan. 8, 2020
Hi @grzegorzmalinowski, I’m not sure I completely understand your question. When you are calling the decorated function as in the example, you are passing arg values to the function, or not if desired. You are providing the argument at the time of calling the function, which is one interpretation of “new”. It is possible to have structured the decorator to handle the arguments or keyword arguments in unique or separate ways also. Meaning do one thing with arguments and another with the keyword arguments. But I don’t think I’m catching your meaning.
grzegorzmalinowski on Jan. 8, 2020
Hi Chris, thank you for the answer. Since I’ve just started my journey with decorators, it might be that I was imprecise in fact. Please see my code. I’d like to change alpha (from predefined scalar to array/or any new value).
@opt_alpha
def update_weights(gradient_type='GD'):
def MSE_GD(aug_data, response, weights, prediction, alpha=learning_rate):
if np.ndim(alpha) != 0:
alpha = alpha[:,np.newaxis]
gradient = 2/response.size*((prediction - response)@aug_data)
direction = -1*gradient
weights = weights + alpha*direction
return weights
if gradient_type == 'GD':
return MSE_GD
def opt_alpha(function):
alpha = np.array([0,1,2])
def weights_wrapper(*args, **kwargs):
result = function(*args, **kwargs)
return result
return weights_wrapper
Chris Bailey RP Team on Jan. 9, 2020
Hi @grzegorzmalinowski, I think you would need to add the alpha keyword argument to your decorator “result”, after **kwargs
. Please excuse my simplifying your function.
def opt_alpha(function):
alpha = [0,1,2]
def weights_wrapper(*args, **kwargs):
result = function(*args, **kwargs, alpha=alpha)
return result
return weights_wrapper
@opt_alpha
def print_alpha(response=25, weights="normal", alpha=0):
print(alpha)
print(response)
print(weights)
When calling you would get.
>>> print_alpha()
[0, 1, 2]
25
normal
The alpha argument will be passed to the function. But to take this a bit further have you looked at the original article that Geir Arne wrote, that my course is based on? Primer on Python Decorators, there is a section on Decorators with Arguments. It discusses when “decorating” a function, you could add an argument to the decorator, which could be the direction you are looking for. I hope this helps.
Become a Member to join the conversation.
Vanam on May 8, 2019
I think in the example, we can have the wrapper to accept only args, def wrapper(*args): With this also, we can pass with or without arguments while calling the decorated function (Please correct me if i am wrong)