如何从列表的列表中制作一个平面列表?

我想知道在Python中是否有一个捷径可以从列表的列表中做出一个简单的列表。

我可以在 for 循环中这样做,但也许有一些很酷的"单行线"?我试着用reduce(),但我得到一个错误。

代码

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)

错误信息

解决办法

给出一个列表l

flat_list = [item for sublist in l for item in sublist]

这意味着:

flat_list = []
for sublist in l:
    for item in sublist:
        flat_list.append(item)

比目前发布的快捷方式要快。(l是要扁平化的列表。)

下面是相应的函数:

flatten = lambda l: [item for sublist in l for item in sublist]

作为证据,你可以使用标准库中的timeit模块:

$ python -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 3: 143 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 969 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 3: 1.1 msec per loop

解释:当有L个子列表时,基于+的快捷方式(包括在sum中的隐含使用)必然是O(L**2)--因为中间结果列表不断变长,每一步都要分配一个新的中间结果列表对象,并且必须将前一个中间结果中的所有项目复制过来(以及在最后添加一些新项目)。因此,为简单起见,在不实际丧失一般性的情况下,假设你有L个子列表,每个列表有I个项目:第一个I个项目来回复制L-1次,第二个I个项目复制L-2次,以此类推;总的复制次数是I乘以x的总和,为x从1到L排除,即I * (L*2)/2

列表理解只是生成一个列表,一次,并将每个项目复制过来(从它原来的位置到结果列表)也正好一次。

评论(29)

作者的说明:这是不高效的。但是很有趣,因为monoids很不错。它不适合用于生产Python代码。

>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]

这只是对第一个参数中传递的迭代器中的元素进行求和,将第二个参数视为求和的初始值(如果没有给出,则使用 "0 "代替,这种情况下会出现错误)。

因为你在对嵌套的列表求和,你实际上得到了[1,3]+[2,4]作为sum([1,3],[2,4]],[])的结果,这等于[1,3,2,4]

注意这只对列表的列表有效。对于列表的列表的列表,你需要另一种解决方案。

评论(15)
from functools import reduce #python 3

>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(lambda x,y: x+y,l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

你的例子中的extend()方法修改了x,而不是返回一个有用的值(这是reduce()期望的)。

reduce版本的更快方法是

>>> import operator
>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(operator.concat, l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
评论(6)