为什么在使用 * args 语法的参数列表中,后面的逗号是 SyntaxError?

为什么不能在 Python 中使用带有 *args的逗号? 换句话说,这样可以工作吗

>>> f(1, 2, b=4,)

但这个不是

>>> f(*(1, 2), b=4,)
File "<stdin>", line 1
f(*(1, 2), b=4,)
^
SyntaxError: invalid syntax

Python2和 Python3都是这种情况。

5184 次浏览

让我们看看 语言规范语言规范:

call                 ::=  primary "(" [argument_list [","]
| expression genexpr_for] ")"
argument_list        ::=  positional_arguments ["," keyword_arguments]
["," "*" expression] ["," keyword_arguments]
["," "**" expression]
| keyword_arguments ["," "*" expression]
["," "**" expression]
| "*" expression ["," "*" expression] ["," "**" expression]
| "**" expression
positional_arguments ::=  expression ("," expression)*
keyword_arguments    ::=  keyword_item ("," keyword_item)*
keyword_item         ::=  identifier "=" expression

让我们筛选出我们关心的部分:

call                 ::=  primary "(" [argument_list [","]] ")"
argument_list        ::=  positional_arguments ["," keyword_arguments]
["," "*" expression] ["," keyword_arguments]
["," "**" expression]
positional_arguments ::=  expression ("," expression)*
keyword_arguments    ::=  keyword_item ("," keyword_item)*
keyword_item         ::=  identifier "=" expression

所以,在函数调用的任何参数之后,我们都允许额外的 ,。所以这看起来像是 cpython 实现中的一个 bug。

比如: f(1, *(2,3,4), )应该按照这种语法工作,但是在 CPython 中不工作。


在前面的答案中,艾瑞克链接到 CPython 语法规范,其中包括上述语法的 CPython 实现。下面是:

arglist: (argument ',')* ( argument [',']
| '*' test (',' argument)* [',' '**' test]
| '**' test
)

注意,这个语法是语言规范提出的 不一样语法。我认为这是一个实现错误。


注意,CPython 实现还有其他问题

奇怪的是,规范不允许 f(*(1,2,3), *(4,5,6), *(7,8,9))

当我更深入地研究这个问题时,我认为规范的这一部分需要一些修正。这是允许的: f(x=1, *(2,3)),但这不是: f(x=1, 2, 3)


为了帮助解决最初的问题,在 CPython 中,如果不使用 *args**kwargs特性,可以在后面加一个逗号。我同意这很无聊。

经过一些讨论,吉多·范罗苏姆 评论:

加上这个我得加1分。我觉得不需要人工呼吸。某些地方已经支持在定义后面加逗号,所以我不认为它会捕获错误。在暂停期间,我们可能太严格了。

随后,提交了 作者: Mark Dickinson,所以这在 Python3.6.0 alpha 1中得到了修复。