为什么Python在关键字参数之后允许* args?

时间:2019-09-19 20:29:20

标签: python python-3.x

示例:

def foo(a, b=2, *args, **kwargs): pass

为什么这不会导致SyntaxError? * args不会捕获其他非关键字参数,因为在关键字参数之后传递它们是非法的。

对于python3.x,* args,** kwargs在这种情况下的正确用法如下所示:

def foo(a, *args, b=2, **kwargs): pass

感谢您对这种奇怪行为的任何见识。

编辑:

感谢Jab向我指出PEP 3102,它简要地解释了此行为。看看吧!

还要感谢jsbueno的出色解释,由于其详尽性,我将其更新为最佳答案。

2 个答案:

答案 0 :(得分:3)

由于多种原因,它已在3.X中实现。我可以询问的最佳方法是
PEP 3102

还请查看Python 3.0.1文档中的New Syntax部分。

TLDR:

  

命名参数出现在   参数列表中的* args必须在调用中使用关键字语法指定。您也可以在参数列表中使用裸露的*表示   您不接受变长参数列表,但是您确实有   仅关键字的参数。

答案 1 :(得分:1)

给出:

def foo(a, b=2, *args, **kwargs): pass

b不是仅用于关键字的参数-它只是一个参数,其参数可以是位置或名称,但具有默认值。无法将任何值传递给args并忽略您建议的签名中传递b或不按顺序传递b的情况。

该签名是有意义的,并且非常明确-您可以从0到n个位置参数传递,但是如果您传递2个或更多,则第二个参数将分配给“ b”,并结束故事。 如果您传递0个位置参数,您仍然可以将值分配给“ a”或“ b”作为命名参数,但是尝试尝试类似以下操作:foo(0, 1, 2, a=3, b=4)将失败,因为尝试将多个值传递给两个参数。

位置:

def foo(a, *args, b=2, **kwargs): pass

这也是一个明确的情况:第一个位置参数转到“ a”,其他位置参数转到“ args”,并且您只能将值作为命名参数传递给“ b”。

Python 3.8随附的签名定义中新的/语法为此提供了更大的灵活性,允许人们要求将“ a”和“ b”作为仅位置参数传递。同样,没有歧义:

def foo(a, b=2, /, *args, **kwargs): pass

对此新语法感到奇怪:允许将命名参数传递给“ a”和“ b”,但是命名参数将作为“ kwargs”中的键/值对出现-而局部变量“ a”和“ b”将被分配唯一的位置参数:

def foo(a, b=2, /, *args, **kwargs): 
    print(a, b, args, kwargs)

...

In [9]: foo(1, 2, a=3, b=4)                                                                                                                                               
1 2 () {'a': 3, 'b': 4}

使用传统语法时,您会询问-def foo(a, b=2, *args, **kwargs):-如果尝试过,则会出现TypeError:

In [11]: foo(1,2, a=3, b=4)                                                                                                                                               
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-d002c7717dba> in <module>
----> 1 foo(1,2, a=3, b=4)

TypeError: foo() got multiple values for argument 'a'