如何将列表的列表转化为扁平化列表?
我有一个列表,里面又包含了很多列表,像这样:
[
[1, 2, 3],
[4, 5, 6],
[7],
[8, 9]
]
我该怎么把它变成一个简单的列表,得到 [1, 2, 3, 4, 5, 6, 7, 8, 9]
呢?
如果你的这个列表是通过嵌套列表推导式生成的,问题可以通过调整推导式来更简单直接地解决;可以看看这个链接 如何从列表推导式得到一个平坦的结果,而不是嵌套列表?。
这里最常见的解决方案通常只会把嵌套列表的一个“层级”展开。想要完全展开一个深层嵌套的结构,可以参考这个链接 展开一个不规则(任意嵌套)的列表,里面有更全面的解决方案(一般是递归的)。
32 个回答
作者的说明: 这个方法效率很低。但很有趣,因为单元(monoids)真是太棒了。
>>> xss = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> sum(xss, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
sum
这个函数的作用是把可迭代对象xss
里的元素加起来,并且用第二个参数[]
作为求和的初始值。(默认的初始值是0
,但那不是一个列表。)
因为你是在对嵌套列表求和,所以当你执行sum([[1,3],[2,4]],[])
时,结果实际上是[1,3]+[2,4]
,也就是[1,3,2,4]
。
需要注意的是,这个方法只适用于列表中的列表。如果是列表中的列表中的列表,你就需要其他的方法了。
你可以使用 itertools.chain()
这个工具:
>>> import itertools
>>> list2d = [[1,2,3], [4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain(*list2d))
或者你可以使用 itertools.chain.from_iterable()
,这个方法不需要用 *
这个符号来展开列表:
>>> import itertools
>>> list2d = [[1,2,3], [4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain.from_iterable(list2d))
这种方法比 [item for sublist in l for item in sublist]
更容易理解,而且看起来也更快:
$ python3 -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))'
20000 loops, best of 5: 10.8 usec per loop
$ python3 -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 5: 21.7 usec per loop
$ python3 -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 5: 258 usec per loop
$ python3 -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;from functools import reduce' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 5: 292 usec per loop
$ python3 --version
Python 3.7.5rc1
一个名为 xss
的列表里面包含了多个列表,我们可以用一种叫做嵌套的 列表推导式 的方法把它们合并成一个平坦的列表:
flat_list = [
x
for xs in xss
for x in xs
]
上面的写法其实等同于:
flat_list = []
for xs in xss:
for x in xs:
flat_list.append(x)
这里是对应的函数:
def flatten(xss):
return [x for xs in xss for x in xs]
这是最快的方法。
为了证明这一点,我们可以使用标准库里的 timeit
模块,结果显示:
$ python -mtimeit -s'xss=[[1,2,3],[4,5,6],[7],[8,9]]*99' '[x for xs in xss for x in xs]'
10000 loops, best of 3: 143 usec per loop
$ python -mtimeit -s'xss=[[1,2,3],[4,5,6],[7],[8,9]]*99' 'sum(xss, [])'
1000 loops, best of 3: 969 usec per loop
$ python -mtimeit -s'xss=[[1,2,3],[4,5,6],[7],[8,9]]*99' 'reduce(lambda xs, ys: xs + ys, xss)'
1000 loops, best of 3: 1.1 msec per loop
解释一下:基于 +
的方法(包括在 sum
中隐含的使用)在有 L 个子列表的情况下,时间复杂度是 O(L**2)
。这是因为每次生成中间结果列表时,它的长度会不断增加,每一步都会分配一个新的中间结果列表对象,并且之前的所有项都必须被复制到新的列表中(还要在末尾添加一些新的项)。所以,简单来说,如果你有 L 个子列表,每个子列表有 M 个项:第一个 M 个项会被复制 L-1
次,第二个 M 个项会被复制 L-2
次,以此类推;总的复制次数是 M 乘以从 1 到 L-1 的和,也就是 M * (L**2)/2
。
而列表推导式只会生成一个列表,所有的项也只会被复制一次(从它们原来的位置到结果列表中)。