多个`for`列表推导的顺序为什么是这样的?

4
我知道在Python3中,在嵌套的列表推导式中正确使用多个 for 的方法如下:
lista = [[[1,2],[3],[4,5,6]],[[7],[8,9]]]

flatlista = [i for k in lista for j in k for i in j]
# results with [1, 2, 3, 4, 5, 6, 7, 8, 9]

但是我的自然语言直觉非常反感。 我本来(错误地)期望代码应该是:
flatlista = [i for i in j for j in k for k in lista]

错误的版本听起来几乎像英语,并且从左到右以一个流读取。正确的版本需要一些嵌套阅读技巧,跳过左右以包含意义。

为什么语法是这样的?为什么语言是这样构建的?


6
我实际上看到了完全相反的情况。按照它实现的方式,你是从左到右阅读的,并且(除了理解体i之外)永远不需要向右看以查看可用的内容。你写for k in lista,你知道你有可用的k,所以你接下来执行for j in k,并知道你有可用的j等等。在你的版本中,你实际上必须从右到左阅读,从外部到内部。 - poke
2
话虽如此,我能理解你的困惑。对我来说,最初的期望也是它会从外向内读取,只是因为许多编程语言都是这样工作的。 - poke
1
我同意OP的看法,第二个排序方式更直观。对我来说,这部分Python编程语言违反了最小惊奇原则,这在Python中很不寻常! - saxbophone
2个回答

11
因为这就是PEP 202 -- 列表推导式设定的方式。然而,PEP并没有完全解释为什么这样做,因为它是作为事后想法创建的;讨论已经在开发列表上进行了多年,甚至在PEP流程被创建之前。

首先,顺序反映了你在Python代码中嵌套for循环和if语句的顺序:

for k in lista:
    for j in k:
        for i in j:

这使得如果您已经习惯了该顺序,那么这将非常自然。
看一下有关该功能的最初讨论,其他语言中似乎存在该顺序的先例。实际上,Haskell也具有相同的顺序每个连续的生成器都会完善前一个生成器的结果
当然,在某些时候,提案的发起人Tim Peters表示,今天使用的顺序对来说是显而易见的,请参见此帖子
我已经多次发布了我对今天的Python的建议翻译,希望它被字面理解而不是引申理解。这个嵌套了“for”循环,最左边的是最外层的,因此涉及到所有级别的排序语义。为什么这在所有级别上都成为了一个争论点,我无法理解。

1
啊,这个图形非常清晰易懂,很容易记住。谢谢@martijn pieters! - Sam

2

只需记住《卡萨布兰卡》中著名的台词:“在这个世界上所有城镇的酒馆中...”然后记住Python版本是从相反方向开始的。

[ginjoint for town in world for ginjoint in town] 

因为谁希望他们的编程语言听起来像有史以来最伟大的电影?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接