【问题标题】:Why is a trailing comma a SyntaxError in an argument list that uses *args syntax?为什么使用 *args 语法的参数列表中的尾随逗号是 SyntaxError?
【发布时间】:2013-06-05 21:42:18
【问题描述】:

为什么不能在 Python 中使用带有 *args 的尾随逗号?换句话说,这行得通

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

但这不是

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

Python 2 和 Python 3 都是这种情况。

【问题讨论】:

  • 可能与(1,) 创建元组的方式有关。
  • 显然关键字 args 是无关紧要的。 f(*args,) 也是不允许的。
  • @user2246674 “为什么”并非无关紧要——这正是问题的重点。 Python 等语言的语法经过精心设计,在可读性、易用性、易解析性、向后兼容性等方面投入了大量考虑。除了 i> 在*args 之后,对此原因感到好奇是合理的。如果您不同意,那么还有许多其他问题需要回答。
  • @user4815162342:我不同意。我认为规范可以在这里使用一些工作。 f(x=1, *(2, 3)) 是允许的,但 f(x=1, 2, 3) 是不允许的。允许f(1, 2, x=3,),但不允许f(*(1, 2), x=3,)
  • @sharth 由于您自己的回答表明实现与规范不符,因此不妨说应该修复的是实现。无论哪种方式,这都与询问当前行为为何如此的问题的合法性正交。这个问题的答案可能是一个简单的疏忽(正如我在第一条评论中提到的)或一个实现错误——Python 语法可能经过精心设计,但没有人是完美的。

标签: python syntax python-internals


【解决方案1】:

我们来看看language specification

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 实现中的一个错误。

类似:f(1, *(2,3,4), ) 应该根据这个语法工作,但在 CPython 中不行。


在较早的答案中,Eric 链接到 CPython grammar specification,其中包括上述语法的 CPython 实现。如下:

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

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


请注意,CPython 实现还存在其他问题。这也应该支持:f(*(1,2,3), *(4,5,6))

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

当我对此进行更多研究时,我认为规范的这一部分需要一些修正。这是允许的:f(x=1, *(2,3)),但这不是:f(x=1, 2, 3)


也许对最初的问题有所帮助,在 CPython 中,如果您不使用 *args**kwargs 功能,则可以使用逗号结尾。我同意这是蹩脚的。

【讨论】:

  • 必须对此表示赞同。这是我第一次看到有人拿出 Python 的语言规范来支持答案。
  • 一般来说答案很好。但我不确定你说f(*(1,2,3), *(4,5,6))“应该支持”是什么意思——在我看来,这两种语法(语言规范和 cpython 语法)都不允许这样做。
  • @EdwardLoper 可能指的是 | "*" expression ["," "*" expression] ["," "**" expression] 的规范语法行。方括号中的第一个子句看起来像是错误地到达那里,该行应该是| "*" expression ["," "**" expression]。正如 sharth 所说,规范确实需要一些工作。
  • 这个 issue 于 2010-12-12 提交到 python bug tracker 被拒绝:bugs.python.org/issue10682
  • @dr.:感谢您在跟踪器中找到有关此内容的信息。我没有机会环顾四周。看起来您引用的错误应该已作为此错误的副本关闭:bugs.python.org/issue9232,他们将其标记为已取代 10682。
【解决方案2】:

issue 9232 讨论了这个错误之后,Guido van Rossum commented

我在添加此内容时 +1。我不认为它需要 PEP。在某些地方已经支持定义中的尾随逗号,所以我不相信它会捕获错误的论点。在暂停期间,我们可能过于严格了。

随后,a patch by Mark Dickinson 被提交。所以这现在在 Python 3.6.0 alpha 1 中得到了修复。

【讨论】:

  • 太棒了!我最初在实际代码中遇到了这个问题,我花了一些时间才弄清楚为什么 Python 告诉我我有语法错误。
猜你喜欢
  • 2012-07-20
  • 1970-01-01
  • 2019-05-09
  • 1970-01-01
  • 2011-12-21
  • 2016-01-25
相关资源
最近更新 更多