简体   繁体   中英

How can I send unknown list of arguments to a python decorator?

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?

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM