列表理解中的双重迭代

Tho*_*asH 202 python list-comprehension

在Python中,您可以在列表推导中使用多个迭代器,例如

[(x,y) for x in a for y in b]
Run Code Online (Sandbox Code Playgroud)

对于一些合适的序列a和b.我知道Python列表推导的嵌套循环语义.

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

[x for x in a for a in b]
Run Code Online (Sandbox Code Playgroud)

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

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

a=[[1,2],[3,4]]
Run Code Online (Sandbox Code Playgroud)

列表理解表达式将实现此结果:

[1,2,3,4]
Run Code Online (Sandbox Code Playgroud)

?? (请仅列出理解答案,因为这是我想要找到的).

Cid*_*ide 150

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

>>> [x for b in a for x in b] # Works fine
Run Code Online (Sandbox Code Playgroud)

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

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

  • `[x for b in a for x in b]` 这一直是关于 python 的问题。这个语法太落后了。“x for x in y”的一般形式总是在 for 后面直接包含变量,将变量提供给 for 左侧的表达式。一旦你进行了双重理解,你最近迭代的变量突然变得如此“遥远”。很尴尬,读起来根本不自然 (32认同)

Ska*_*kam 139

我希望这对别人a,b,x,y有帮助,因为对我没有多大意义!假设你有一个充满句子的文本,你想要一组单词.

# Without list comprehension
list_of_words = []
for sentence in text:
    for word in sentence:
       list_of_words.append(word)
return list_of_words
Run Code Online (Sandbox Code Playgroud)

我喜欢将列表理解视为水平拉伸代码.

尝试将其分解为:

# List Comprehension 
[word for sentence in text for word in sentence]
Run Code Online (Sandbox Code Playgroud)

  • “计算机科学中只有两个难题:缓存失效和命名。” -菲尔·卡尔顿 (7认同)
  • 我更喜欢这个顺序:“[字母对单词中的字母对单词中的单词对句子中的句子]” (4认同)
  • 或者,您可以: [[句子中的逐字] for 文本中的句子] (2认同)
  • @Saskia 不完全是。这只会给你相同的输入。你明白为什么吗? (2认同)

Tho*_*asH 113

哎呀,我想我找到了anwser:我没有足够关心哪个循环是内部的,哪个是外部的.列表理解应该是:

[x for b in a for x in b]
Run Code Online (Sandbox Code Playgroud)

得到所需的结果,是的,一个当前值可以是下一个循环的迭代器:-).对不起噪音.

  • 列表理解语法不是Python的亮点之一. (61认同)
  • 如果你在每个'for'之前添加换行符,它看起来很干净. (14认同)
  • 哇,这完全颠倒了我头脑中的意义. (13认同)
  • @Glenn是的,除了简单的表达式之外,它很容易让人费解。 (2认同)

Dim*_*nek 44

迭代器的顺序可能看起来违反直觉.

举个例子: [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)
Run Code Online (Sandbox Code Playgroud)

  • 多么大开眼界!! (4认同)
  • 我的理解是,这样做的原因是“如果将理解编写为嵌套 for 循环,则列出的第一个迭代是将键入的最顶层迭代”。这是违反直觉的原因是 OUTER 循环(如果写为嵌套 for 循环则为最顶层)出现在括号列表/字典(理解的对象)的内部。相反,INNER 循环(当编写为嵌套 for 循环时为最内层)恰恰是推导式中最右边的循环,并且以这种方式出现在推导式的 OUTSIDE 处。 (2认同)
  • 抽象地写成我们有 `[(loop 2) (loop 1) (loop 2)]` 和 `(loop 1) = for i in range(3)` 和 `(loop 2) = for x in foo(i ):` 和 `(循环 2 中的输出) = str(x)`。 (2认同)

Sła*_*art 20

这种记忆技术对我有很大帮助:

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

现在,你可以想想[R E打开+ Ø uter环作为唯一的[R飞行Ø刻申

知道上面的内容,即使是 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)]
Run Code Online (Sandbox Code Playgroud)

因为以上只是一个:

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
Run Code Online (Sandbox Code Playgroud)

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

a = [[1,2],[3,4]]
[i2    for i1 in a      for i2 in i1]
which return [1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)

对于另一个嵌套级别

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]

Run Code Online (Sandbox Code Playgroud)

等等

  • 谢谢,但您所描述的实际上是涉及的迭代器是独立的简单情况。事实上,在您的示例中,您可以_以任何顺序_使用迭代器,并将获得相同的结果列表(模排序)。我更感兴趣的情况是嵌套列表,其中一个迭代器成为下一个迭代器的可迭代器。 (2认同)

Mar*_*oma 19

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]
Run Code Online (Sandbox Code Playgroud)

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

这个问题的第二个"问题"是b从列表理解中"泄露"出来.在第一次成功列表理解之后b == [3, 4].

  • 有趣的一点.我对此感到惊讶:`x ='hello';``[x for x in xrange(1,5)];``print x#x现在是4` (3认同)
  • 这种泄漏在Python 3中得到修复:/sf/ask/293923451/ (2认同)

ste*_*ven 10

如果要保留多维数组,则应该嵌套数组括号.请参阅下面的示例,其中一个添加到每个元素.

>>> 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]
Run Code Online (Sandbox Code Playgroud)


Mar*_*tim 8

在我第一次尝试时,我永远无法写出双重列表理解。阅读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]
Run Code Online (Sandbox Code Playgroud)

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

# wrong logic
(((d for d in c) for c in b) for b in a)
Run Code Online (Sandbox Code Playgroud)

然而,这不是Python 实现嵌套列表的方式。相反,该实现将第一个块视为完全独立的,然后将fors 和ins 从上而下(而不是自下而上)链接到一个块中,即

# right logic
d: (for b in a, for c in b, for d in c)
Run Code Online (Sandbox Code Playgroud)

请注意,最深的嵌套级别 ( 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()]
Run Code Online (Sandbox Code Playgroud)


小智 5

我觉得这更容易理解

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

result: [1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)