Python列表推导式中的for循环

18

我在阅读Python wikibook,但对以下内容感到困惑:

列表推导式支持多个for语句。它将按顺序评估所有对象的项,并循环遍历较短的对象(如果其中一个对象比其余对象长)。

>>> item = [x+y for x in 'cat' for y in 'pot']
>>> print item
['cp', 'co', 'ct', 'ap', 'ao', 'at', 'tp', 'to', 'tt']
我理解嵌套的for循环用法,但是我不理解

...如果一个对象比其他对象更长,则会循环遍历较短的对象

这是什么意思?(更短、更长...)


7
那个解释没有用。写那段话的人可能把它和zip的行为混淆了。 - user2357112
@user2357112 我认为你是对的。只需查找zip函数。使用zip函数时,对象的长度很重要,因为它将循环遍历较短的对象。谢谢。 - ChandlerQ
2个回答

22
这种嵌套循环创建两个序列的笛卡尔积。试一下:
>>> [x+y for x in 'cat' for y in 'potty']
['cp', 'co', 'ct', 'ct', 'cy', 'ap', 'ao', 'at', 'at', 'ay', 'tp', 'to', 'tt', 'tt', 'ty']
>>> [x+y for x in 'catty' for y in 'pot']
['cp', 'co', 'ct', 'ap', 'ao', 'at', 'tp', 'to', 'tt', 'tp', 'to', 'tt', 'yp', 'yo', 'yt']

在上面的列表推导式中,内部的 'x'(即 for x in 'cat' 部分)与此示例中的外部 for x in 'cat': 是相同的。
>>> li=[]
>>> for x in 'cat':
...    for y in 'pot':
...       li.append(x+y)
# li=['cp', 'co', 'ct', 'ap', 'ao', 'at', 'tp', 'to', 'tt']

因此,使一个循环变短或变长的效果与使两个嵌套循环中的“x”或“y”循环变长相同:

>>> li=[]
>>> for x in 'catty':
...    for y in 'pot':
...       li.append(x+y)
... 
>>> li==[x+y for x in 'catty' for y in 'pot']
True

在每种情况下,较短的序列会被重复循环,直到较长的序列用尽。这与zip不同,后者在较短的序列结束时会终止配对。

编辑

评论中似乎存在关于嵌套循环和zip的混淆。

嵌套循环:

如上所示,这个:

[x+y for x in '12345' for y in 'abc']

这与两个嵌套的“for”循环相同,其中“x”为外部循环。

嵌套循环将执行内部的y循环,在外部循环中的x范围内执行。

所以:

>>> [x+y for x in '12345' for y in 'ab']
    ['1a', '1b',   # '1' in the x loop
     '2a', '2b',   # '2' in the x loop, b in the y loop
     '3a', '3b',   # '3' in the x loop, back to 'a' in the y loop
     '4a', '4b',   # so on
     '5a', '5b'] 

您可以使用itertools中的product来获得相同的结果:

>>> from itertools import product
>>> [x+y for x,y in product('12345','ab')]
['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b']

Zip是类似的,但在较短的序列用尽后停止:

>>> [x+y for x,y in zip('12345','ab')]
['1a', '2b']
>>> [x+y for x,y in zip('ab', '12345')]
['a1', 'b2']

你可以使用itertools进行zip,直到最长序列用尽,但结果是不同的:
>>> import itertools
>>> [x+y for x,y in itertools.zip_longest('12345','ab',fillvalue='*')]
['1a', '2b', '3*', '4*', '5*'] 

@drewk 嗯,我理解这个过程,只是对于维基百科所说的更长/更短的事情感到困惑,因为循环显然与对象的长度无关。 - ChandlerQ
Wikibooks 的说法是错误的。它所做的是循环遍历两个序列中较长的那一个,正如我的示例清楚地展示的那样。 - dawg
@drewk 是的,循环较短对象的函数是zip函数,对吧?现在我明白了。 - ChandlerQ
它所做的是循环遍历两个序列中较长的那一个,正如我的示例清楚地展示的那样。你的意思是什么?你是说Wikibooks的陈述是不正确的,因为它没有循环遍历较短的那一个,而是较长的那一个? - RussW

6
好的,Python文档没有提到任何短/长案例:http://docs.python.org/2/tutorial/datastructures.html#list-comprehensions。在列表理解中使用两个“for”意味着有两个循环。 @drewk指出的示例是正确的。
为了说明起见,让我复制它:
>>> [x+y for x in '123' for y in 'pot']
['1p', '1o', '1t', '2p', '2o', '2t', '3p', '3o', '3t']
>>>
>>> [x+y for x in '1' for y in 'pot']
['1p', '1o', '1t']
>>>

在这两种情况下,第一个“for”形成了外部循环,第二个“for”形成了内部循环。这是唯一的常数。

1
是的,我已经谷歌了列表推导式,但我没有找到任何更短或更长的东西...这本维基书所说的方式让我感到困惑,两个对象都将被循环处理,而与对象的长度无关,对吗? - ChandlerQ
1
同意。而且这个例子也证明了两种情况下最终的值是相同的(+1)。 - Manoj Pandey

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