为什么函数装饰器中的 **kwargs 值与函数中的值不同?
Why is **kwargs value in a function decorator different from the value in a function?
我正在尝试编写一个函数装饰器,它打印参数,用它调用一个函数,我注意到一件事。如果我们创建装饰器,它只打印 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
具体命名为 a
、b
和 c
,因此不会捕获这些名称,但您的装饰器包装器名称为 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
.
剩余的参数,a
,b
和 c
是 'captured' 由 foo
函数的命名参数。您在 foo
中单独定义了它们,因此它们被专门分配给了它们。它们不是 wrapper()
定义的一部分,因此无法在那里捕获它们。
请注意,您没有向调用传递任何 positional 参数,因此 *args
变量为 empty。改为传递一些位置参数:
>>> 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 个值仍然在 a
、b
和 c
中,但是 extra 位置值 42
在 foo
中的 args
中仍然可用。在包装器中,没有明确命名的参数,它们 all 最终在 *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
和5
也出现在args
中,'d'
和'g'
键直接出现在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}}
这是为什么?为什么 print kwargs 在装饰器中打印前三个参数,但在函数中却不打印?
kwargs
捕获函数签名中未明确命名的任何 key=value
参数。 foo
具体命名为 a
、b
和 c
,因此不会捕获这些名称,但您的装饰器包装器名称为 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
.
剩余的参数,a
,b
和 c
是 'captured' 由 foo
函数的命名参数。您在 foo
中单独定义了它们,因此它们被专门分配给了它们。它们不是 wrapper()
定义的一部分,因此无法在那里捕获它们。
请注意,您没有向调用传递任何 positional 参数,因此 *args
变量为 empty。改为传递一些位置参数:
>>> 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 个值仍然在 a
、b
和 c
中,但是 extra 位置值 42
在 foo
中的 args
中仍然可用。在包装器中,没有明确命名的参数,它们 all 最终在 *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
和5
也出现在args
中,'d'
和'g'
键直接出现在kwargs
字典中.