协慌网

登录 贡献 社区

在 Python 中列出列表中的平面列表

我想知道是否有一条快捷方式可以在 Python 列表中列出一个简单的列表。

我可以在 for 循环中做到这一点,但也许有一些很酷的 “单行”?我用reduce尝试了,但是我收到了一个错误。

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

错误信息

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'

答案

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

意思是:

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 项目来回复制 L-1 次,第二个 I 项目 L-2 次,依此类推; 总复制数是 I 乘以 x 的总和,从 1 到 L 排除,即I * (L**2)/2

列表理解只生成一个列表一次,并将每个项目(从其原始居住地点到结果列表)复制一次。

你可以使用itertools.chain()

>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain(*list2d))

或者,在 Python> = 2.6 上,使用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]更具可读性[item for sublist in l for item in sublist]并且看起来更快:

[[email protected]]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))'
10000 loops, best of 3: 24.2 usec per loop
[[email protected]]$ 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: 45.2 usec per loop
[[email protected]]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 488 usec per loop
[[email protected]]$ 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: 522 usec per loop
[[email protected]]$ python --version
Python 2.7.3

作者请注意 :这是低效的。但很有趣,因为单子很棒。它不适合生产 Python 代码。

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

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

因为你是对嵌套列表求和,实际上得到[1,3]+[2,4]作为sum([[1,3],[2,4]],[]) ,它等于[1,3,2,4]

请注意,仅适用于列表列表。对于列表列表,您需要另一种解决方案。