繁体   English   中英

列表理解中的双重迭代

[英]Double Iteration in List Comprehension

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

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

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

我的问题是:推导中的一个迭代器可以引用另一个吗? 换句话说:我能不能有这样的东西:

[x for x in a for a in b]

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

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

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

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

[1,2,3,4]

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

我希望这对其他人a,b,x,y帮助,因为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

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

尝试将其分解为:

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

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

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

哎呀,我想我找到了答案:我没有足够注意哪个循环是内部的,哪个是外部的。 列表理解应该是这样的:

[x for b in a for x in b]

以获得所需的结果,是的,一个当前值可以作为下一个循环的迭代器。

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

举个例子: [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)

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]

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

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

现在你可以认为关于R E打开+ O 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)]

因为以上只是一个:

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

用于重复一个嵌套列表/结构,TECHNIC是相同的:为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]

等等

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

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

在我第一次尝试时,我永远无法写出双重列表理解。 阅读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 实现嵌套列表的方式。 相反,该实现将第一个块视为完全独立的,然后将for s 和in s 从上到下(而不是自下而上)链接到一个块中,即

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

我觉得这更容易理解

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

result: [1, 2, 3, 4]

此外,您可以对当前访问的输入列表的成员该成员内部的元素使用相同的变量。 然而,这甚至可能使它更(列表)难以理解。

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

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

这个 flatten_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]

暂无
暂无

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

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