返回语句中的 Python 元组解包
问题描述
Python 语言(尤其是 3.x)允许非常通用的解包迭代,一个简单的例子是
The Python language (especially 3.x) allows very general unpacking of iterables, a simple example of which is
a, *rest = 1, 2, 3
多年来,这种拆包已逐渐普及(参见例如 PEP 3132 和 PEP 448),使其可以被越来越多地使用情况.因此,我惊讶地发现以下在 Python 3.6 中是无效语法(在 Python 3.7 中仍然如此):
Over the years, this unpacking has been gradually generalized (see e.g. PEP 3132 and PEP 448), allowing it to be used in more and more circumstances. As so, I was surprised to discover that the following is invalid syntax in Python 3.6 (and remains so in Python 3.7):
def f():
rest = [2, 3]
return 1, *rest # Invalid
我可以通过将返回的元组封装在括号中来使其工作,如下所示:
I can make it work by encapsulating the returned tuple in parentheses like so:
def f():
rest = [2, 3]
return (1, *rest) # Valid
我在 return
语句中使用它的事实似乎很重要,因为
The fact that I use this in a return
statement seems to be important, as
t = 1, *rest
确实是合法的,结果相同,带括号和不带括号.
is indeed legal and results in the same with and without parentheses.
这个案例只是被 Python 开发人员忘记了,还是有什么原因导致这个案例是无效的语法?
Have this case simply been forgotten by the Python developers, or are there any reason why this case is invalid syntax?
这打破了我认为我与 Python 语言之间的重要约定.考虑以下(也是有效的)解决方案:
This breaks an important contract I thought I had with the Python language. Consider the following (also valid) solution:
def f():
rest = [2, 3]
t = 1, *rest
return t
通常当我有这样的代码时,我认为 t
是一个临时名称,我应该能够摆脱它,只需将 t
替换为其定义的底线.但是在这种情况下,这会导致代码无效
Normally when I have code like this, I consider t
to be a temporary name, which I ought to be able to get rid of simply be replacing t
in the bottom line with its definition. In this case though, this leads to the invalid code
def f():
rest = [2, 3]
return 1, *rest
在返回值周围加上括号当然没什么大不了的,但通常只需要额外的括号来区分几种可能的结果(分组).在这里情况并非如此,因为省略括号不会产生其他一些不需要的行为,而是根本没有行为.
It's of course no big deal to have to place parentheses around the return value, but usually additional parentheses are only needed to distinguish between several possible outcomes (grouping). Here this is not the case, as leaving out the parentheses does not produce some other unwanted behavior, but rather no behavior at all.
自 Python 3.8 起(参见 此列表中的第 7 项),上面讨论的通用语法现在是有效的.
Since Python 3.8 (see item 7 on this list), the generalized syntax discussed above is now valid.
解决方案
我怀疑这是一个意外,基于来自 此提交 适用于 Python 3.2.
I suspect this is an accident, based on the comments from this commit for Python 3.2.
该提交使赋值表达式能够采用 testlist_star_expr
产生式(允许无括号解包),但让 return 语句采用 testlist
产生式.我怀疑提交只是错过了这个(可能还有其他位置,但我现在专注于 return_stmt
生产).
That commit enabled the assignment expression to take a testlist_star_expr
production (what allows the unparenthesized unpacking), but left the return statement taking a testlist
production. I suspect the commit just missed this (and possibly other locations, but I'm focusing on the return_stmt
production for now).
我继续修改 Python 语法/语法文件以允许这样做.所有测试都继续通过,包括 test_grammar.py
文件中的测试(但这似乎并不十分详尽).
I went ahead and modified the Python Grammar/Grammar file to allow this. All of the tests continue to pass, including those in the test_grammar.py
file (but this doesn't seem terribly exhaustive).
如果您好奇,这是我所做的更改.随意克隆或下载 my fork.
If you're curious, this is the change I made. Feel free to clone or download my fork.
更新:我已经提交了 bpo 问题 和 pull request 用于返回(和 yield)解包.
UPDATE: I've submitted a bpo issue and a pull request for the return (and yield) unpacking.
相关文章