令我惊讶的是,我注意到在函数调用中,我可以用甚至不是有效的 python 标识符的dict
字符串来解压 a 。
让我感到惊讶的是,因为参数名称必须是标识符,因此允许函数调用解包**kwargs
具有非标识符的 a 且没有运行时错误,这似乎并不健康(因为它可能会将问题埋藏得比实际发生的地方更深)。
除非能够做到这一点有实际用途,在这种情况下我的问题就变成“那会有什么用途?”。
考虑这个函数:
def foo(**kwargs):
first_key, first_val = next(iter(kwargs.items()))
print(f"{first_key=}, {first_val=}")
return kwargs
Run Code Online (Sandbox Code Playgroud)
这表明,在函数调用中,您无法解压dict
具有整数键的 a,这是预期的。
>>> t = foo(**{1: 2, 3: 4})
TypeError Traceback (most recent call last)
...
TypeError: foo() keywords must be strings
Run Code Online (Sandbox Code Playgroud)
另一方面,真正出乎意料且令人惊讶的是,您可以使用dict
字符串键解压 a,即使这些不是有效的 python 标识符:
>>> t = foo(**{'not an identifier': 1, '12': 12, ',(*&$)': 100})
first_key='not an identifier', first_val=1
>>> t
{'not an identifier': 1, '12': 12, ',(*&$)': 100}
Run Code Online (Sandbox Code Playgroud)
看起来这更像是一个kwargs
问题而不是拆包问题。例如,人们不会遇到同样的问题foo
:
def foo(a, b):
print(a + b)
foo(**{"a": 3, "b": 2})
# 5
foo(**{"a": 3, "b": 2, "c": 4})
# TypeError: foo() got an unexpected keyword argument 'c'
foo(**{"a": 3, "b": 2, "not valid": 4})
# TypeError: foo() got an unexpected keyword argument 'not valid'
Run Code Online (Sandbox Code Playgroud)
但kwargs
使用时,这种灵活性是有代价的。看起来该函数首先尝试弹出并映射所有命名参数,然后将剩余项传递到被调用dict
的kwargs
. 由于所有关键字都是字符串(但所有字符串都不是有效关键字),因此第一个检查很容易 - keywords must be strings
。除此之外,由作者决定如何处理 中的剩余项目kwargs
。
def bar(a, **kwargs):
print(locals())
bar(a=2)
# {'a': 2, 'kwargs': {}}
bar(**{"a": 3, "b": 2})
# {'a': 3, 'kwargs': {'b': 2}}
bar(**{"a": 3, "b": 2, "c": 4})
# {'a': 3, 'kwargs': {'b': 2, 'c': 4}}
bar(**{1: 3, 3: 4})
# TypeError: keywords must be strings
Run Code Online (Sandbox Code Playgroud)
综上所述,确实存在不一致之处,但不是缺陷。一些相关讨论: