问题 在Python中列出列表中的平面列表
我想知道是否有一条快捷方式可以在Python列表中列出一个简单的列表。
我可以在for循环中做到这一点,但也许有一些很酷的“单行”?我尝试过 减少,但是我收到了一个错误。
码
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'
7599
2018-06-04 20:30
起源
答案:
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
说明:基于的快捷方式 +
(包括隐含用途 sum
),是必要的, O(L**2)
当有L个子列表时 - 随着中间结果列表越来越长,每个步骤都会分配一个新的中间结果列表对象,并且必须复制前一个中间结果中的所有项目(以及添加的一些新项目)最后)。所以(为了简单而没有实际的失去一般性)说你有每个项目的L个子列表:第一个I项目来回复制L-1次,第二个I项目L-2次,依此类推;总复制数是I乘以x的总和,从1到L排除,即 I * (L**2)/2
。
列表理解只生成一个列表一次,并将每个项目(从其原始居住地点到结果列表)复制一次。
3001
2018-06-04 20:37
您可以使用 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]
并且看起来也更快:
[me@home]$ 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
[me@home]$ 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
[me@home]$ 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
[me@home]$ 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
[me@home]$ python --version
Python 2.7.3
1083
2018-06-04 21:06
作者请注意:这是低效的。但很有趣,因为单子很棒。它不适合生产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]
。
请注意,仅适用于列表列表。对于列表列表,您需要另一种解决方案。
638
2018-06-04 20:35
我测试了大多数建议的解决方案 perfplot (我的一个宠物项目,基本上是一个包装 timeit
),并找到了
list(itertools.chain.from_iterable(a))
成为最快的解决方案(如果连接的列表超过10个)。

重现情节的代码:
import functools
import itertools
import numpy
import operator
import perfplot
def forfor(a):
return [item for sublist in a for item in sublist]
def sum_brackets(a):
return sum(a, [])
def functools_reduce(a):
return functools.reduce(operator.concat, a)
def itertools_chain(a):
return list(itertools.chain.from_iterable(a))
def numpy_flat(a):
return list(numpy.array(a).flat)
def numpy_concatenate(a):
return list(numpy.concatenate(a))
perfplot.show(
setup=lambda n: [list(range(10))] * n,
kernels=[
forfor, sum_brackets, functools_reduce, itertools_chain, numpy_flat,
numpy_concatenate
],
n_range=[2**k for k in range(16)],
logx=True,
logy=True,
xlabel='num lists'
)
131
2017-07-26 09:38
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]
100
2018-06-04 20:35
以下是适用的一般方法 数字, 字符串, 嵌套 列表和 杂 容器。
码
from collections import Iterable
def flatten(items):
"""Yield items from any nested iterable; see Reference."""
for x in items:
if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
for sub_x in flatten(x):
yield sub_x
else:
yield x
注意:在Python 3中, yield from flatten(x)
可以取代 for sub_x in flatten(x): yield sub_x
演示
lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(flatten(lst)) # nested lists
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
mixed = [[1, [2]], (3, 4, {5, 6}, 7), 8, "9"] # numbers, strs, nested & mixed
list(flatten(mixed))
# [1, 2, 3, 4, 5, 6, 7, 8, '9']
参考
- 此解决方案是从配方中修改的 Beazley,D。和B. Jones。食谱4.14,Python Cookbook 3rd Ed。,O'Reilly Media Inc. Sebastopol,CA:2013。
- 发现较早 所以发帖,可能是最初的示范。
55
2017-11-29 04:14
我接受我的陈述。总和不是赢家。虽然列表很小但速度更快。但是,较大的列表会使性能显着下降。
>>> timeit.Timer(
'[item for sublist in l for item in sublist]',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10000'
).timeit(100)
2.0440959930419922
总和版本仍然运行超过一分钟,它还没有完成处理!
对于中型名单:
>>> timeit.Timer(
'[item for sublist in l for item in sublist]',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
).timeit()
20.126545906066895
>>> timeit.Timer(
'reduce(lambda x,y: x+y,l)',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
).timeit()
22.242258071899414
>>> timeit.Timer(
'sum(l, [])',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
).timeit()
16.449732065200806
使用小列表和timeit:number = 1000000
>>> timeit.Timer(
'[item for sublist in l for item in sublist]',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
).timeit()
2.4598159790039062
>>> timeit.Timer(
'reduce(lambda x,y: x+y,l)',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
).timeit()
1.5289170742034912
>>> timeit.Timer(
'sum(l, [])',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
).timeit()
1.0598428249359131
31
2018-06-04 20:46
为什么使用extend?
reduce(lambda x, y: x+y, l)
这应该工作正常。
25
2018-06-04 20:38