理解列表理解,以便在python中展平列表列表

gbr*_*ios 10 python list-comprehension list generator

我发现这种理解非常适用于展平列表列表:

>>> list_of_lists = [(1,2,3),(2,3,4),(3,4,5)]
>>> [item for sublist in list_of_lists for item in sublist]
[1, 2, 3, 2, 3, 4, 3, 4, 5]
Run Code Online (Sandbox Code Playgroud)

我喜欢这比使用更好itertools.chain(),但我无法理解它.我试过用括号括起来的部分,看看我是否可以减少复杂性,但现在我只是更加困惑:

>>> [(item for sublist in list_of_lists) for item in sublist]
[<generator object <genexpr> at 0x7ff919fdfd20>, <generator object <genexpr> at 0x7ff919fdfd70>, <generator object <genexpr> at 0x7ff919fdfdc0>]

>>> [item for sublist in (list_of_lists for item in sublist)]
[5, 5, 5]
Run Code Online (Sandbox Code Playgroud)

我觉得我很难理解,因为我不太明白发电机是如何工作的......我的意思是,我以为我做了,但现在我真的很怀疑.就像我说的,我喜欢这个成语是多么紧凑,这正是我需要的,但我不愿意使用我不理解的代码.

谁能解释一下究竟发生了什么?

the*_*eye 11

列表理解

当我第一次开始列表理解时,我读到的就像英语句子一样,我能够很容易地理解它们.例如,

[item for sublist in list_of_lists for item in sublist]
Run Code Online (Sandbox Code Playgroud)

可以像

for each sublist in list_of_lists and for each item in sublist add item
Run Code Online (Sandbox Code Playgroud)

此外,过滤部分可以读作

for each sublist in list_of_lists and for each item in sublist add item only if it is valid
Run Code Online (Sandbox Code Playgroud)

而相应的理解将是

[item for sublist in list_of_lists for item in sublist if valid(item)]
Run Code Online (Sandbox Code Playgroud)

发电机

它们就像地雷一样,只有在使用next协议调用时才会触发.它们类似于函数,但是直到引发异常或函数结束,它们才会耗尽,并且可以一次又一次地调用它们.重要的是,它们保留了先前调用和当前调用之间的状态.

生成器和函数之间的区别在于,生成器使用yield关键字将值赋给调用者.在生成器表达式的情况下,它们类似于列表推导,第一个表达式是实际值"被屈服".

有了这个基本的理解,如果我们看一下你在问题中的表达方式,

[(item for sublist in list_of_lists) for item in sublist]
Run Code Online (Sandbox Code Playgroud)

您正在将列表理解与生成器表达式混合使用.这将是这样的

for each item in sublist add a generator expression which is defined as, for every sublist in list_of_lists yield item
Run Code Online (Sandbox Code Playgroud)

这不是你心中的想法.并且由于不迭代生成器表达式,因此生成器表达式对象将按原样添加到列表中.由于在没有使用下一个协议调用的情况下不会对它们进行评估,因此它们不会产生任何错误(如果有的话,除非它们有语法错误).在这种情况下,它将产生运行时错误,因为sublist尚未定义.

另外,在最后一种情况下,

[item for sublist in (list_of_lists for item in sublist)]
Run Code Online (Sandbox Code Playgroud)
for each sublist in the generator expression, add item and the generator expression is defined as for each item in sublist yield list_of_lists.
Run Code Online (Sandbox Code Playgroud)

for循环将迭代任何可迭代的下一个协议.因此,将评估生成器表达式,并且item将始终是迭代中的最后一个元素,sublist并且您将在列表中添加它.这也会产生运行时错误,因为尚未定义子列表.


Mar*_*ers 10

从左到右阅读for循环,就好像它们是嵌套的一样.左边的表达式是在最终列表中生成每个值的表达式:

for sublist in list_of_lists:
    for item in sublist:
        item  # added to the list
Run Code Online (Sandbox Code Playgroud)

列表推导还支持if测试以过滤使用的元素; 这些也可以看作嵌套语句,与for循环相同.

通过添加括号,您更改了表达式; 括号中的所有内容现在都是要添加的左侧表达式:

for item in sublist:
    (item for sublist in list_of_lists)  # added to the list
Run Code Online (Sandbox Code Playgroud)

for像这样的循环是生成器表达式.它的工作方式与列表理解完全相同,只是它不构建列表.而是根据需要生成元素.您可以向生成器表达式询问下一个值,然后是下一个值,等等.

在这种情况下,必须有一个预先存在的sublist对象才能使其工作; list_of_lists毕竟,外环已经不再结束了.

您的最后一次尝试转换为:

for sublist in (list_of_lists for item in sublist):
    item  # aded to the list
Run Code Online (Sandbox Code Playgroud)

list_of_lists是循环的生成器表达式中的循环元素for item in sublist.同样,sublist必须已经存在才能使其发挥作用.然后循环将预先存在的内容添加item到最终列表输出中.

在你的情况下,显然sublist是一个包含3个项目的列表; 你的最终名单产生了3个元素.item是必然的5,所以你5输出3次.