简体   繁体   English

列表理解中的双重迭代

[英]Double Iteration in List Comprehension

In Python you can have multiple iterators in a list comprehension, like在 Python 中,列表推导式中可以有多个迭代器,例如

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

for some suitable sequences a and b.对于一些合适的序列 a 和 b。 I'm aware of the nested loop semantics of Python's list comprehensions.我知道 Python 列表推导式的嵌套循环语义。

My question is: Can one iterator in the comprehension refer to the other?我的问题是:推导中的一个迭代器可以引用另一个吗? In other words: Could I have something like this:换句话说:我能不能有这样的东西:

[x for x in a for a in b]

where the current value of the outer loop is the iterator of the inner?其中外循环的当前值是内循环的迭代器?

As an example, if I have a nested list:例如,如果我有一个嵌套列表:

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

what would the list comprehension expression be to achieve this result:列表理解表达式是什么来实现这个结果:

[1,2,3,4]

?? ?? (Please only list comprehension answers, since this is what I want to find out). (请只列出理解性答案,因为这是我想知道的)。

I hope this helps someone else since a,b,x,y don't have much meaning to me!我希望这对其他人a,b,x,y帮助,因为a,b,x,y对我没有多大意义! Suppose you have a text full of sentences and you want an array of words.假设您有一个充满句子的文本,并且您想要一组单词。

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

I like to think of list comprehension as stretching code horizontally.我喜欢将列表理解视为水平拉伸代码。

Try breaking it up into:尝试将其分解为:

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

Example:例子:

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

This also works for generators这也适用于发电机

>>> 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?

To answer your question with your own suggestion:用您自己的建议回答您的问题:

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

While you asked for list comprehension answers, let me also point out the excellent itertools.chain():当你要求列表理解的答案时,让我也指出优秀的 itertools.chain():

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

Gee, I guess I found the anwser: I was not taking care enough about which loop is inner and which is outer.哎呀,我想我找到了答案:我没有足够注意哪个循环是内部的,哪个是外部的。 The list comprehension should be like:列表理解应该是这样的:

[x for b in a for x in b]

to get the desired result, and yes, one current value can be the iterator for the next loop.以获得所需的结果,是的,一个当前值可以作为下一个循环的迭代器。

Order of iterators may seem counter-intuitive.迭代器的顺序可能看起来违反直觉。

Take for example: [str(x) for i in range(3) for x in foo(i)]举个例子: [str(x) for i in range(3) for x in foo(i)]

Let's decompose it:让我们分解它:

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)

ThomasH has already added a good answer, but I want to show what happens: 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]

I guess Python parses the list comprehension from left to right.我猜 Python 从左到右解析列表理解。 This means, the first for loop that occurs will be executed first.这意味着,第一个发生的for循环将首先执行。

The second "problem" of this is that b gets "leaked" out of the list comprehension.第二个“问题”是b从列表理解中“泄露”出来。 After the first successful list comprehension b == [3, 4] .在第一次成功的列表理解之后b == [3, 4]

This memory technic helps me a lot:这种记忆技术对我有很大帮助:

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

And now you can think about R eturn + O uter-loop as the only R ight O rder现在你可以认为关于R E打开+ O uter环作为唯一的[R飞行刻申

Knowing above, the order in list comprehensive even for 3 loops seem easy:知道上面的内容,即使是 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)]

because the above is just a:因为以上只是一个:

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

for iterating one nested list/structure, technic is the same: for a from the question:用于重复一个嵌套列表/结构,TECHNIC是相同的:为a从所述的问题:

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

for one another nested level对于另一个嵌套级别

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]

and so on等等

If you want to keep the multi dimensional array, one should nest the array brackets.如果要保留多维数组,则应嵌套数组括号。 see example below where one is added to every element.请参见下面的示例,其中每个元素都添加了一个。

>>> 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]

I could never write double list comprehension on my first attempt.在我第一次尝试时,我永远无法写出双重列表理解。 Reading into PEP202 , it turns out the reason is that it was implemented in the opposite way you would read it in English.阅读PEP202 ,结果证明它的实现方式与您用英语阅读的方式相反 The good news is that it is a logically sound implementation, so once you understand the structure, it's very easy to get right.好消息是它是一个逻辑上合理的实现,所以一旦你理解了结构,就很容易做到正确。

Let a, b, c, d be successively nested objects.设 a, b, c, d 是连续嵌套的对象。 For me, the intuitive way to extend list comprehension would mimic English:对我来说,扩展列表理解的直观方法是模仿英语:

# 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]

In other words, you'd be reading from the bottom up, ie换句话说,你会从下往上阅读,即

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

However this is not how Python implements nested lists.然而,这不是Python 实现嵌套列表的方式。 Instead, the implementation treats the first chunk as completely separate, and then chains the for s and in s in a single block from the top down (instead of bottom up), ie相反,该实现将第一个块视为完全独立的,然后将for s 和in s 从上到下(而不是自下而上)链接到一个块中,即

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

Note that the deepest nested level ( for d in c ) is farthest from the final object in the list ( d ).请注意,最深的嵌套级别( for d in c )距离列表中的最终对象( d )最远。 The reason for this comes from Guido himself :这样做的原因来自 于圭多本人

The form [... for x... for y...] nests, with the last index varying fastest, just like nested for loops.形式[... for x... for y...]嵌套,最后一个索引变化最快,就像嵌套 for 循环一样。

Using Skam's text example, this becomes even more clear:使用 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()]

I feel this is easier to understand我觉得这更容易理解

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

result: [1, 2, 3, 4]

Additionally, you could use just the same variable for the member of the input list which is currently accessed and for the element inside this member.此外,您可以对当前访问的输入列表的成员该成员内部的元素使用相同的变量。 However, this might even make it more (list) incomprehensible.然而,这甚至可能使它更(列表)难以理解。

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

First for x in input is evaluated, leading to one member list of the input, then, Python walks through the second part for x in x during which the x-value is overwritten by the current element it is accessing, then the first x defines what we want to return.首先for x in input被评估,导致for x in input一个成员列表,然后,Python 遍历for x in x的第二部分for x in x期间 x 值被它正在访问的当前元素覆盖,然后第一个x定义我们想要返回什么。

This flatten_nlevel function calls recursively the nested list1 to covert to one level.这个 flatten_nlevel 函数递归调用嵌套的 list1 以转换到一个级别。 Try this out试试这个

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)

output:输出:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM