返回语句中的 Python 元组解包
Posted
技术标签:
【中文标题】返回语句中的 Python 元组解包【英文标题】:Why is starred iterable unpacking in a return statement invalid syntax without parentheses before Python 3.8? 【发布时间】:2018-04-26 14:45:54 【问题描述】:Python 语言(尤其是 3.x)允许非常通用的解包迭代,一个简单的例子是
a, *rest = 1, 2, 3
多年来,这种拆包已逐渐普及(参见例如PEP 3132 和PEP 448),使其可以在越来越多的情况下使用。因此,我惊讶地发现以下在 Python 3.6 中是无效语法(在 Python 3.7 中仍然如此):
def f():
rest = [2, 3]
return 1, *rest # Invalid
我可以通过将返回的元组封装在括号中来使其工作,如下所示:
def f():
rest = [2, 3]
return (1, *rest) # Valid
我在return
语句中使用它这一事实似乎很重要,因为
t = 1, *rest
确实是合法的,结果相同,带括号和不带括号。
这个案例只是被 Python 开发人员忘记了,还是有什么原因导致这个案例是无效的语法?
我为什么关心
这打破了我认为我与 Python 语言之间的重要约定。考虑以下(也是有效的)解决方案:
def f():
rest = [2, 3]
t = 1, *rest
return t
通常当我有这样的代码时,我认为t
是一个临时名称,我应该能够摆脱它,只需将底线中的t
替换为它的定义。但是在这种情况下,这会导致代码无效
def f():
rest = [2, 3]
return 1, *rest
在返回值周围加上括号当然没什么大不了的,但通常只需要额外的括号来区分几种可能的结果(分组)。在这里情况并非如此,因为省略括号不会产生其他一些不需要的行为,而是根本没有行为。
更新
从 Python 3.8 开始(参见 this list 的第 7 项),上面讨论的通用语法现在有效。
【问题讨论】:
这实际上是grammar syntax 的结果。 你也不能只返回 *rest,这是无效的语法。 @lapisdecor 是的,但这与t = *rest
无效的事实一致。另外,return *rest
和t = *rest
并不代表任何实际的拆包,所以我不认为这是不允许的问题。如果允许,*rest
本身只会是 tuple(rest)
的令人困惑的语法。
这种情况不仅仅发生在return
上。在 yield
参数、下标、augmented 赋值(但不是常规赋值)的 RHS 以及 for
语句中的 in
右侧也禁止拆包,尽管所有这些位置都允许使用无括号的元组,因为这些东西的语法使用expression_list
而不是starred_expression
。
注意t = *rest
和t = *rest,
之间的区别。后者是有效的。
【参考方案1】:
我怀疑这是一个意外,基于来自 this commit 的 Python 3.2 的 cmets。
该提交使赋值表达式采用testlist_star_expr
产生式(允许无括号解包),但让return 语句采用testlist
产生式。我怀疑提交只是错过了这个(可能还有其他位置,但我现在专注于return_stmt
生产)。
我继续修改 Python 语法/语法文件以允许这样做。所有测试都继续通过,包括 test_grammar.py
文件中的测试(但这似乎并不十分详尽)。
如果你好奇,this is the change I made。随意克隆或下载my fork。
更新:我已经提交了 bpo issue 和 pull request 用于退货(和产量)拆包。
【讨论】:
您是否针对此更改提出拉取请求? 还没有。想看看这是否正确地解决了@jmd_dk 所追求的问题,也许在发送之前看看其他一些案例(如yield_stmt
生产)。
创建一个“修复”似乎有点不成熟,如果你认为这是一个意外,可以先提交一个错误报告bugs.python.org
修复设置出现在 Python 3.8 中以上是关于返回语句中的 Python 元组解包的主要内容,如果未能解决你的问题,请参考以下文章