为什么函数装饰器中的** kwargs值与函数中的值不同?

时间:2016-05-22 10:16:34

标签: python decorator kwargs

我正在尝试编写一个函数装饰器,它打印一个函数被调用的参数,我注意到了一件事。如果我们创建装饰器,它只是打印kwargs,它将起作用。所以

def counted(fn):
    def wrapper(*args, **kwargs):
        print kwargs
        return fn(*args, **kwargs)
    return wrapper
@counted
def foo(a, b = 4, c = 'blah-blah', *args, **kwargs):
    return

foo(a=1, b=2, c=3, args=[4, 5], kwargs={'d': 6, 'g': 12.9})

输出

{'a': 1, 'c': 3, 'b': 2, 'args': [4, 5], 'kwargs': {'d': 6, 'g': 12.9}}

但是,如果在没有装饰器的情况下调用该函数,但是在函数内打印参数,则输出不同:

#@counted
def foo(a, b = 4, c = 'blah-blah', *args, **kwargs):
print kwargs    #added this line
return

输出:

{'args': [4, 5], 'kwargs': {'d': 6, 'g': 12.9}}

为什么?为什么装饰器中的打印kwargs打印前三个参数,但不在函数中?

1 个答案:

答案 0 :(得分:1)

kwargs会捕获在功能签名中未明确命名的任何key=value参数。 foo具体指定abc,因此不会捕获这些名称,但您的装饰包装名称​​ none ,因此所有内容都会被捕获kwargs。请注意,这包括您传入通话的args=...kwargs=... 参数

您需要将print添加到两个位置,并且您会发现装扮者不是在这里做任何特别的事情:

>>> @counted
... def foo(a, b = 4, c = 'blah-blah', *args, **kwargs):
...     print kwargs
...
>>> foo(a=1, b=2, c=3, args=[4, 5], kwargs={'d': 6, 'g': 12.9})
{'a': 1, 'c': 3, 'b': 2, 'args': [4, 5], 'kwargs': {'d': 6, 'g': 12.9}}
{'args': [4, 5], 'kwargs': {'d': 6, 'g': 12.9}}

您也不打印args,因此您不会看到传递位置参数的内容。让我们再添加一些印刷语句来展示实际情况:

def counted(fn):
    def wrapper(*args, **kwargs):
        print 'counted args: {!r}'.format(args)
        print 'counted kwargs: {!r}'.format(kwargs)
        return fn(*args, **kwargs)
    return wrapper

@counted
def foo(a, b = 4, c = 'blah-blah', *args, **kwargs):
    print 'foo a, b, c: {!r}, {!r}, {!r}'.format(a, b, c)
    print 'foo args: {!r}'.format(args)
    print 'foo kwargs: {!r}'.format(kwargs)

运行此功能,您将获得更好的图片:

>>> foo(a=1, b=2, c=3, args=[4, 5], kwargs={'d': 6, 'g': 12.9})
counted args: ()
counted kwargs: {'a': 1, 'c': 3, 'b': 2, 'args': [4, 5], 'kwargs': {'d': 6, 'g': 12.9}}
foo a, b, c: 1, 2, 3
foo args: ()
foo kwargs: {'args': [4, 5], 'kwargs': {'d': 6, 'g': 12.9}}

再次注意,'args''kwargs'都是自己的关键字参数;它们最终都成为kwargs 词典中的键。你可以将它们命名为其他任何东西,它们仍会以新名称结束:

>>> foo(a=1, b=2, c=3, spam=[4, 5], eggs={'d': 6, 'g': 12.9})
counted args: ()
counted kwargs: {'a': 1, 'c': 3, 'b': 2, 'eggs': {'d': 6, 'g': 12.9}, 'spam': [4, 5]}
foo a, b, c: 1, 2, 3
foo args: ()
foo kwargs: {'eggs': {'d': 6, 'g': 12.9}, 'spam': [4, 5]}

您没有为那里的*args**kwargs catch-all变量指定值,您在 {{1>中指定了要捕获的更多任意关键字参数 }}

其余参数kwargsab已被捕获'通过c函数的命名参数。您在foo中单独定义了这些内容,因此将它们专门分配。它们不属于foo定义的一部分,所以它们无法在那里被捕获。

请注意,您没有将任何位置参数传递给调用,因此wrapper()变量为空。传递一些位置参数:

*args

现在,前3个值仍以>>> foo(1, 2, 3, 42, args=[4, 5], kwargs={'d': 6, 'g': 12.9}) counted args: (1, 2, 3, 42) counted kwargs: {'args': [4, 5], 'kwargs': {'d': 6, 'g': 12.9}} foo a, b, c: 1, 2, 3 foo args: (42,) foo kwargs: {'args': [4, 5], 'kwargs': {'d': 6, 'g': 12.9}} ab结尾,但额外位置值c仍可用位于42的{​​{1}}。在包装器中,没有明确命名的参数,它们所有最终都在args

如果您希望将foo*args值应用为单独的参数,则需要使用相同的args=[..]kwargs={...}在不使用捕获名称的情况下调用时的前缀

*

现在**>>> foo(1, 2, 3, 42, *[4, 5], **{'d': 6, 'g': 12.9}) counted args: (1, 2, 3, 42, 4, 5) counted kwargs: {'d': 6, 'g': 12.9} foo a, b, c: 1, 2, 3 foo args: (42, 4, 5) foo kwargs: {'d': 6, 'g': 12.9} 也会显示在4中,5args键会直接显示在'd'字典中。< / p>