为什么Python的列表推导循环顺序是反向的?

20
>>> my_list = [[[[1, 2, 3], [4, 5, 6], ]]]
>>> [a for d in my_list for c in d for b in c for a in b]
[1, 2, 3, 4, 5, 6]

等同于

>>> my_list = [[[[1, 2, 3], [4, 5, 6], ]]]
>>> new_list = []
>>> for d in my_list:
...     for c in d:
...         for b in c:
...             for a in b:
...                 new_list.append(a)
... print(new_list):
[1, 2, 3, 4, 5, 6]

从左到右阅读时,此语法似乎相反。根据PEP 202,“形式为[... for x... for y...]的嵌套与嵌套for循环的最后一个索引变化最快一样是“正确的”。

看起来选择这个次序是因为嵌套的for循环也是按照外部到内部的顺序书写的。

然而,由于列表推导式(上面示例中的a)的表达式部分对应于嵌套循环的最内层(上面示例中的new_list.append(a)),所以在这两种情况下,看起来离这个表达式最近的for _ in _应该是一样的,即它应该是for a in b,然后向外扩展:

>>> my_list = [[[[1, 2, 3], [4, 5, 6], ]]]
>>> [a for a in b for b in c for c in d for d in my_list]
NameError: name 'b' is not defined

这样可以使变化最快的循环更接近行动,可以这么说。这也使得按照更逻辑分步方式从左到右阅读更容易。

这是否是用户中普遍的看法?或者是否有人有一个好的反对意见来证明当前语法实现确实是“正确的”?


6
这是一个BDFL的声明。除非Guido在此发帖,否则所有回答都只是猜测和个人意见,我很担心。 - Martijn Pieters
6
我正在想象一个替代宇宙,在那里事情是以另一种方式完成的,另一个用户(例如称为“hemaj”的用户)来到 Stack Overflow 发布一个问题,“为什么 Python 的列表推导循环顺序与 for 循环顺序相反?” - DSM
3
我预计这个问题会被关闭,但“与现有的for循环顺序一致”就是Guido需要的全部答案。除非有一个非常强有力的理由去反对这一点。 - Tim Peters
1
我认为需要注意的是,[a for b in my_list for a in b]与例如[a for a in (b for b in my_list)]执行的操作不同。前者实际上对my_list执行了扁平化操作,而后者仅对(my_list的元素的元素)进行了解包和重新打包。在你思考之前这并不明显。 - mwfearnley
1
很明显,后者只是解包,这不是意思。我们有一个变量a,那么a是什么?啊,a for a in b,好的。但是b是什么?现在我们期望的是a for a in b for b in c。有道理,不是吗? - peter
显示剩余2条评论
1个回答

8

请考虑:

[leaf for branch in tree for leaf in branch]

它的展开方式如下:

for branch in tree:
    for leaf in branch:
        yield leaf

如果我们用另一种方式编写它
[leaf for leaf in branch for branch in tree]

从某种程度上来说,用通俗易懂的英语可能更有意义,但一个可能的反驳是:这里使用了“分支”名称,但尚未定义


1
这是一个很好的观点,因为for foo in bar总是定义foo,并期望bar已经被定义。并不是说我的观点改变了 :/ - jameh
9
然而,我可以对你提出同样的论点 - 即“leaf”这个名字被使用却没有(至今)定义。(假设我们是从左到右阅读) - jameh
好的,这里“leaf”将是“自由变量”,就像谓词演算中一样 - 例如,{x:P(x)}的读法类似于“使得P(x)成立的x的集合”。 - wim
4
正是这个原因,leaf 应该放在最前面,感谢您提供的比较。我仍然认为模式 {leaf: leaf 在 {branch: branchtree}} 比 {leaf: branchtree, leafbranch} 更对称、更易读,特别是当你有三个或更多层嵌套时。 - jameh
2
@wim “分支”这个名称在没有定义的情况下被使用,难道“叶子”不是一样的吗?for循环本身就是这样,先使用再声明。 - dawid

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