Python元组在return语句中解包


76

Python语言(尤其是3.x)允许非常广泛地拆解可迭代对象,其中一个简单的示例是

a, *rest = 1, 2, 3

多年来,这种拆包已逐渐普及(例如参见PEP 3132PEP 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(请参阅此列表中的第7项)开始,上面讨论的通用语法现在有效。


4
这实际上是语法语法的结果,而不是其他任何结果。
cs95

您也不能只返回* rest,这是无效的语法。
lapisdecor

3
@lapisdecor是的,但这与t = *rest无效的事实是一致的。此外,return *restt = *rest并不代表任何实际的拆包,所以我并不觉得这是不允许的问题。如果允许的话,*rest那么它本身就只会是一个令人困惑的语法tuple(rest)
jmd_dk

这不仅仅是发生return。Unpackings也不能出现在一个yield说法,一个标,一个的RHS增强转让(但不是常规分配),并在右面infor声明,尽管加括号的元组被允许在所有这些位置,因为语法对于那些事情用expression_list代替starred_expression
user2357112支持Monica17年

3
注意之间的差异t = *restt = *rest,。后者有效。
senderle '17

Answers:


38

根据针对Python 3.2的这次提交的评论,我怀疑这是一次意外。

该提交使赋值表达式可以进行testlist_star_expr生产(允许使用不带括号的解包),但是保留return语句进行testlist生产。我怀疑提交只是错过了(可能还有其他地点,但是我现在只专注于return_stmt制作)。

我继续修改了Python Grammar / Grammar文件以允许此操作。所有测试都继续通过,包括test_grammar.py文件中的测试(但这似乎并不十分详尽)。

如果您很好奇,这就是我所做的更改。随意克隆或下载我的fork

更新:我已经提交了一个bpo问题和一个关于退货(和收益)拆包的拉取请求


3
您是否已通过此更改提出拉动请求?
马丁·彼得斯

还没。想看看这是否可以正确解决@jmd_dk之后的问题,并可能yield_stmt在发送之前查看其他几种情况(例如生产环境)。
David Cuthbert

2
创建一个“修复程序”似乎还为时过早,
Chris_Rands

3
该修复程序将出现在Python 3.8中
Michael Scott Cuthbert
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.