gpt4 book ai didi

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

转载 作者:太空宇宙 更新时间:2023-11-03 16:33:56 25 4
gpt4 key购买 nike

我正在尝试编写一个函数装饰器,它打印参数,用它调用一个函数,我注意到一件事。如果我们创建装饰器,只打印 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}}

这是为什么呢?为什么装饰器中的 print kwargs 会打印前三个参数,但在函数中却不会?

最佳答案

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 捕获所有变量指定值,您指定了要捕获的更多任意关键字参数 kwargs

其余参数,abcfoo 的命名参数“捕获” > 功能。您在 foo 中单独定义了它们,因此它们被专门分配。它们不是 wrapper() 定义的一部分,因此无法在那里捕获它们。

请注意,您没有向调用传递任何位置参数,因此*args变量为。而是传递一些位置参数:

>>> 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}}

现在前 3 个值仍然以 abc 结束,但是额外位置值 42fooargs 中仍然可用。在包装器中,没有显式命名的参数,它们全部都以*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}

现在45也出现在args中,并且'd'' g' 键直接出现在 kwargs 字典中。

关于python - 为什么函数装饰器中的 **kwargs 值与函数中的值不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37373031/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com