Reputation: 12585
I have created a python decorator as shown below. I want this decorator to accept an unknown list of arguments. But the code below doesn't work.
#!/usr/bin/env python
from functools import wraps
def my_decorator(decorated_function, **kwargs):
print "kwargs = {}".format(kwargs)
@wraps(decorated_function)
def inner_function(*args, **kwargs):
print "Hello world"
return decorated_function(*args, **kwargs)
return inner_function
@my_decorator(arg_1="Yolo", arg2="Bolo")
def my_func():
print "Woo Hoo!"
my_func()
When I run it, I get this error:
File "./decorator_test.py", line 14, in <module>
@my_decorator(arg_1="Yolo", arg2="Bolo")
TypeError: my_decorator() takes exactly 1 argument (0 given)
Why is the **kwargs
not accepting the multiple arguments I'm sending into the decorator? How can I fix it?
Upvotes: 3
Views: 477
Reputation: 37549
Essentially, when you have a decorator that accepts arguments, you're kind of calling it twice, once with the keyword arguments, and then again with just the function to be decorated.
Before the @deco
syntax, you would decorate a function just by passing it into the decorator function
func = deco(func)
And if you wanted to include other keyword arguments, you could just pass them in at the same time
func = deco(func, arg=True, arg2=5)
But the @deco
syntax doesn't allow that, it works more like this, so you're calling a function returned by the decorator function.
func = deco(arg=True, arg2=5)(func)
To do this with the @deco
syntax, you would create a decorator that tests to see whether it was called with just a function (the decoration part), or whether it was called with keyword arguments (which sets up the decorator to be passed a function).
def deco_with_kwargs(func=None, **kwargs):
def deco(func_):
def wrapped_func(*fargs, **fkwargs):
print kwargs
return func_(*fargs, **fkwargs)
return wrapped_func
if func:
return deco(func)
else:
return deco
You could use it like this without any keyword arguments.
@deco_with_args
def my_func():
return 1
Or you could call it like this with keyword arguments. In this case, you're actually calling the decorator function, which returns another decorator function, which is then called to actually decorate the function.
@deco_with_args(test=True)
def my_func():
return 1
Upvotes: 2