返回语句中的 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 *restt = *rest 并不代表任何实际的拆包,所以我不认为这是不允许的问题。如果允许,*rest 本身只会是 tuple(rest) 的令人困惑的语法。 这种情况不仅仅发生在return 上。在 yield 参数、下标、augmented 赋值(但不是常规赋值)的 RHS 以及 for 语句中的 in 右侧也禁​​止拆包,尽管所有这些位置都允许使用无括号的元组,因为这些东西的语法使用expression_list而不是starred_expression 注意t = *restt = *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 元组解包的主要内容,如果未能解决你的问题,请参考以下文章

python中的解包

Python入门教程第58篇 函数进阶之元组解包

Python3 元组解包情况总结

从长度为1的元组解包返回值

Python解包中的默认值

scala 将元组解包到案例类参数和附加的 zip 两个序列中