列表理解中的双重迭代

在Python中,你可以在一个列表推导式中有多个迭代器,比如

[(x,y) for x in a for y in b]

我知道Python的列表推导式的嵌套循环语义。

我的问题是:理解中的一个迭代器可以指向另一个迭代器吗?换句话说:我能得到这样的东西吗?

[x for x in a for a in b]

外部循环的当前值是内部循环的迭代器?

举个例子,如果我有一个嵌套列表:

a=[[1,2],[3,4]]

要实现这个结果,列表理解表达式是什么:

[1,2,3,4]

?? (请只列出理解性的答案,因为这是我想知道的)。

308898 次浏览

哎呀,我想我找到了答案:我没有足够关心哪个循环是内部的,哪个是外部的。列表推导式应该是这样的:

[x for b in a for x in b]

为了得到想要的结果,一个当前值可以作为下一个循环的迭代器。

用你自己的建议来回答你的问题:

>>> [x for b in a for x in b] # Works fine

当你要求列表理解答案时,让我也指出优秀的itertools.chain():

>>> from itertools import chain
>>> list(chain.from_iterable(a))
>>> list(chain(*a)) # If you're using python < 2.6

ThomasH已经补充了一个很好的答案,但我想展示发生了什么:

>>> a = [[1, 2], [3, 4]]
>>> [x for x in b for b in a]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined


>>> [x for b in a for x in b]
[1, 2, 3, 4]
>>> [x for x in b for b in a]
[3, 3, 4, 4]

我猜Python是从左到右解析列表理解的。这意味着,发生的第一个for循环将首先执行。

第二个“问题”是b被“泄露”出列表理解。在第一个成功的列表理解之后b == [3, 4].;

我觉得这样更容易理解

[row[i] for row in a for i in range(len(a))]


result: [1, 2, 3, 4]

假设你有一个充满句子的文本,你想要一个单词数组。

# Without list comprehension
list_of_words = []
for sentence in text:
for word in sentence:
list_of_words.append(word)
return list_of_words

我喜欢将列表理解理解为横向扩展代码。

试着把它分解成:

# List Comprehension
[word for sentence in text for word in sentence]

例子:

>>> text = (("Hi", "Steve!"), ("What's", "up?"))
>>> [word for sentence in text for word in sentence]
['Hi', 'Steve!', "What's", 'up?']

这也适用于生成器

>>> text = (("Hi", "Steve!"), ("What's", "up?"))
>>> gen = (word for sentence in text for word in sentence)
>>> for word in gen: print(word)
Hi
Steve!
What's
up?

迭代器的顺序似乎与直觉相反。

例如:[str(x) for i in range(3) for x in foo(i)]

让我们分解它:

def foo(i):
return i, i + 0.5


[str(x)
for i in range(3)
for x in foo(i)
]


# is same as
for i in range(3):
for x in foo(i):
yield str(x)

如果你想保持多维数组,就应该嵌套数组括号。参见下面的示例,其中每个元素都添加了一个。

>>> a = [[1, 2], [3, 4]]


>>> [[col +1 for col in row] for row in a]
[[2, 3], [4, 5]]


>>> [col +1 for row in a for col in row]
[2, 3, 4, 5]

此外,你可以对输入列表的成员使用相同的变量,该成员当前正在访问而且中的元素。然而,这甚至可能使它更加难以理解。

input = [[1, 2], [3, 4]]
[x for x in input for x in x]

首先计算for x in input,导致输入的一个成员列表,然后,Python遍历第二部分for x in x,在此期间x值被它正在访问的当前元素覆盖,然后第一个x定义我们想要返回的内容。

这个记忆技巧对我帮助很大:

[ <RETURNED_VALUE> <OUTER_LOOP1> <INNER_LOOP2> <INNER_LOOP3> ... <OPTIONAL_IF> ]

现在你可以考虑Return + __abc1utter -loop 作为唯一的Right Order

知道了上面,列表综合的顺序即使是3个循环看起来也很简单:


c=[111, 222, 333]
b=[11, 22, 33]
a=[1, 2, 3]


print(
[
(i, j, k)                            # <RETURNED_VALUE>
for i in a for j in b for k in c     # in order: loop1, loop2, loop3
if i < 2 and j < 20 and k < 200      # <OPTIONAL_IF>
]
)
[(1, 11, 111)]

因为上面只是一个:

for i in a:                         # outer loop1 GOES SECOND
for j in b:                       # inner loop2 GOES THIRD
for k in c:                     # inner loop3 GOES FOURTH
if i < 2 and j < 20 and k < 200:
print((i, j, k))            # returned value GOES FIRST

用于迭代一个嵌套列表/结构,技术是相同的: a从问题:

a = [[1,2],[3,4]]
[i2    for i1 in a      for i2 in i1]
which return [1, 2, 3, 4]

对于另一个嵌套级别

a = [[[1, 2], [3, 4]], [[5, 6], [7, 8, 9]], [[10]]]
[i3    for i1 in a      for i2 in i1     for i3 in i2]
which return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


等等

这个flat_nlevel函数递归调用嵌套的list1来转换到一个级别。试试这个

def flatten_nlevel(list1, flat_list):
for sublist in list1:
if isinstance(sublist, type(list)):
flatten_nlevel(sublist, flat_list)
else:
flat_list.append(sublist)


list1 = [1,[1,[2,3,[4,6]],4],5]


items = []
flatten_nlevel(list1,items)
print(items)

输出:

[1, 1, 2, 3, 4, 6, 4, 5]

在我第一次尝试的时候,我从来没有写过双表理解。读入PEP202,就会发现原因是它是以你在英语中阅读它的相反方式实现的。好消息是,这是一个逻辑上合理的实现,因此一旦您理解了结构,就很容易得到正确的实现。

设a, b, c, d是依次嵌套的对象。对我来说,扩展列表理解的直观方法是模仿英语:

# works
[f(b) for b in a]
# does not work
[f(c) for c in b for b in a]
[f(c) for c in g(b) for b in a]
[f(d) for d in c for c in b for b in a]

换句话说,你将从下往上阅读,即。

# wrong logic
(((d for d in c) for c in b) for b in a)

然而,这< >强不是< / >强 Python如何实现嵌套列表。相反,该实现将第一个块视为完全独立的块,然后从上到下(而不是从下到上)将__abc0和__abc1链接到单个块中,即。

# right logic
d: (for b in a, for c in b, for d in c)

请注意,最深的嵌套层(for d in c)距离列表中的最终对象(d)最远。原因是来自圭多本人:

[... for x... for y...]形式嵌套,最后一个索引变化最快,就像嵌套for循环一样。

使用Skam的文本示例,这变得更加清楚:

# word: for sentence in text, for word in sentence
[word for sentence in text for word in sentence]


# letter: for sentence in text, for word in sentence, for letter in word
[letter for sentence in text for word in sentence for letter in word]


# letter:
#     for sentence in text if len(sentence) > 2,
#     for word in sentence[0],
#     for letter in word if letter.isvowel()
[letter for sentence in text if len(sentence) > 2 for word in sentence[0] for letter in word if letter.isvowel()]