我正在尝试编写一个函数装饰器,它打印一个函数被调用的参数,我注意到了一件事。如果我们创建装饰器,它只是打印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打印前三个参数,但不在函数中?
答案 0 :(得分:1)
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
catch-all变量指定值,您在 {{1>中指定了要捕获的更多任意关键字参数 }}
其余参数kwargs
,a
和b
已被捕获'通过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}}
,a
和b
结尾,但额外位置值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
中,5
和args
键会直接显示在'd'
字典中。< / p>