python:如何从列表列表中生成一个平面列表?

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

我可以在for循环中这样做,但是也许有一些很酷的"一行代码"?我用reduce试过了,但是我得到了一个错误。

代码

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

错误消息

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


给定一个列表列表l

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

这意味着:

1
2
3
4
flat_list = []
for sublist in l:
    for item in sublist:
        flat_list.append(item)

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

对应函数如下:

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

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

1
2
3
4
5
6
$ 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子列表,中间结果列表越来越长,每一步一个新的中间结果列表对象被分配,和所有的物品在前面中间结果必须被复制(以及一些新的添加结束时)。因此,为了简单和不损失一般性,假设每个I项都有L个子列表:第一个I项来回复制L-1次,第二个I项来回复制L-2次,以此类推;总拷贝数为I乘以x从1到L的累加和,即,I * (L**2)/2

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


您可以使用itertools.chain():

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

或者,在Python >=2.6上使用itertools.chain.from_iterable(),它不需要打开列表:

1
2
3
>>> 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]更具可读性,而且似乎也更快:

1
2
3
4
5
6
7
8
9
10
[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


作者注:这是低效的。但有趣的是,因为独角兽非常棒。它不适用于生产Python代码。

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

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

因为要对嵌套列表求和,所以实际上得到的结果是sum([[1,3],[2,4]],[]),它等于[1,3,2,4]

注意,只对列表中的列表有效。对于列表的列表的列表,您需要另一个解决方案。


我使用perfplot(我的一个宠物项目,本质上是一个围绕timeit的包装器)测试了大多数建议的解决方案,并找到了答案

1
functools.reduce(operator.iconcat, a, [])

成为最快的解决方案。(operator.iadd同样快。)

enter image description here

再现图像的程式码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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 functools_reduce_iconcat(a):
    return functools.reduce(operator.iconcat, 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, functools_reduce_iconcat,
        itertools_chain, numpy_flat, numpy_concatenate
        ],
    n_range=[2**k for k in range(16)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )


1
2
3
4
5
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版本的更快方法是

1
2
3
4
>>> 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]


下面是一种适用于数字、字符串、嵌套列表和混合容器的通用方法。

代码

1
2
3
4
5
6
7
8
9
10
11
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

注意:在python3中,yield from flatten(x)可以替换for sub_x in flatten(x): yield sub_x

演示

1
2
3
4
5
6
7
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 &amp; mixed
list(flatten(mixed))
# [1, 2, 3, 4, 5, 6, 7, 8, '9']

参考

这个解决方案是从Beazley, D.和B. Jones的食谱中修改而来的。食谱4.14,Python烹饪书第三版,O'Reilly Media Inc.。塞瓦斯托波尔,CA: 2013。发现较早的SO帖子,可能是最初的演示。


我收回我的话。sum不是赢家。虽然当列表很小的时候会更快。但是随着列表的增大,性能会显著下降。

1
2
3
4
5
>>> 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

sum版本仍然运行了超过一分钟,还没有完成处理!

媒体列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> 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

使用小列表和时间:number=1000000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> 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


如果您想使一个数据结构变平,而又不知道它嵌套的深度,您可以使用iteration_utilities.deepflatten1

1
2
3
4
5
6
7
8
9
>>> from iteration_utilities import deepflatten

>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(deepflatten(l, depth=1))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

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

它是一个生成器,因此您需要将结果转换为list或显式地遍历它。

如果每一个项目本身是可迭代的,你也可以使用iteration_utilities.flatten,它本身只是一个薄薄的包装围绕itertools.chain.from_iterable:

1
2
3
4
>>> from iteration_utilities import flatten
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(flatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

只是添加一些时间(基于Nico Schl?未包含本答案中所述功能的mer答案):

enter image description here

它是一个对数-对数图,以适应跨越的值的巨大范围。定性推理:越低越好。

结果表明,如果迭代器只包含几个内部迭代器,那么sum将是最快的,但是对于长迭代器,只有itertools.chain.from_iterableiteration_utilities.deepflatten或嵌套理解具有合理的性能,而itertools.chain.from_iterable是最快的(Nico Schl?mer已经注意到这一点)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from itertools import chain
from functools import reduce
from collections import Iterable  # or from collections.abc import Iterable
import operator
from iteration_utilities import deepflatten

def nested_list_comprehension(lsts):
    return [item for sublist in lsts for item in sublist]

def itertools_chain_from_iterable(lsts):
    return list(chain.from_iterable(lsts))

def pythons_sum(lsts):
    return sum(lsts, [])

def reduce_add(lsts):
    return reduce(lambda x, y: x + y, lsts)

def pylangs_flatten(lsts):
    return list(flatten(lsts))

def flatten(items):
   """Yield items from any nested iterable; see REF."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            yield from flatten(x)
        else:
            yield x

def reduce_concat(lsts):
    return reduce(operator.concat, lsts)

def iteration_utilities_deepflatten(lsts):
    return list(deepflatten(lsts, depth=1))


from simple_benchmark import benchmark

b = benchmark(
    [nested_list_comprehension, itertools_chain_from_iterable, pythons_sum, reduce_add,
     pylangs_flatten, reduce_concat, iteration_utilities_deepflatten],
    arguments={2**i: [[0]*5]*(2**i) for i in range(1, 13)},
    argument_name='number of inner lists'
)

b.plot()

免责声明:我是那个图书馆的作者


为什么要使用extend?

1
reduce(lambda x, y: x+y, l)

这应该没问题。


似乎与operator.add混淆了!当您将两个列表相加时,正确的术语是concat,而不是add. operator.concat是您需要使用的。

如果你认为功能,它是这样简单::

1
2
3
>>> list2d = ((1, 2, 3), (4, 5, 6), (7,), (8, 9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)

您将看到reduce尊重序列类型,因此当您提供一个元组时,您将返回一个元组。让我们试着用一个列表::

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

啊哈,你得到了一个列表。

如何表现::

1
2
3
>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> %timeit list(itertools.chain.from_iterable(list2d))
1000000 loops, best of 3: 1.36 μs per loop

from_iterable非常快!但与concat相比没有可比性。

1
2
3
>>> list2d = ((1, 2, 3),(4, 5, 6), (7,), (8, 9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop


您的函数不能工作的原因:extend extends array in-place并没有返回它。你仍然可以从返回x,使用一些技巧:

1
reduce(lambda x,y: x.extend(y) or x, l)

注意:扩展比列表中的+更有效。


考虑安装more_itertools包。

1
> pip install more_itertools

它附带了flatten的实现(来源:itertools recipes):

1
2
3
4
5
6
import more_itertools


lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.flatten(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

从2.4版开始,您可以使用more_itertools.collapse(源代码,由abarnet提供)将更复杂的嵌套迭代器压平。

1
2
3
4
5
6
7
lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.collapse(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

lst = [[1, 2, 3], [[4, 5, 6]], [[[7]]], 8, 9]              # complex nesting
list(more_itertools.collapse(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

1
2
3
4
5
6
7
8
9
10
11
def flatten(l, a):
    for i in l:
        if isinstance(i, list):
            flatten(i, a)
        else:
            a.append(i)
    return a

print(flatten([[[1, [1,1, [3, [4,5,]]]], 2, 3], [4, 5],6], []))

# [1, 1, 1, 3, 4, 5, 2, 3, 4, 5, 6]


上面Anil函数的一个不好的特性是,它要求用户总是手动指定第二个参数为空列表[]。这应该是默认值。由于Python对象的工作方式,这些应该在函数内部设置,而不是在参数中设置。

这是一个工作函数:

1
2
3
4
5
6
7
8
9
10
11
12
def list_flatten(l, a=None):
    #check a
    if a is None:
        #initialize with empty list
        a = []

    for i in l:
        if isinstance(i, list):
            list_flatten(i, a)
        else:
            a.append(i)
    return a

测试:

1
2
3
4
5
6
7
In [2]: lst = [1, 2, [3], [[4]],[5,[6]]]

In [3]: lst
Out[3]: [1, 2, [3], [[4]], [5, [6]]]

In [11]: list_flatten(lst)
Out[11]: [1, 2, 3, 4, 5, 6]


下面这些对我来说似乎是最简单的:

1
2
3
4
>>> import numpy as np
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> print (np.concatenate(l))
[1 2 3 4 5 6 7 8 9]


在处理基于文本的可变长度列表时,接受的答案对我不起作用。这里有一个对我有用的替代方法。

1
l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]

接受了无效的答案:

1
2
3
flat_list = [item for sublist in l for item in sublist]
print(flat_list)
['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c', 'xx', 'yyyyyyy']

新提出的解决方案,为我工作:

1
2
3
4
flat_list = []
_ = [flat_list.extend(item) if isinstance(item, list) else flat_list.append(item) for item in l if item]
print(flat_list)
['aaa', 'bb', 'cccccc', 'xx', 'yyyyyyy']

matplotlib.cbook.flatten()将用于嵌套列表,即使嵌套比示例更深。

1
2
3
4
5
import matplotlib
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
print(list(matplotlib.cbook.flatten(l)))
l2 = [[1, 2, 3], [4, 5, 6], [7], [8, [9, 10, [11, 12, [13]]]]]
print list(matplotlib.cbook.flatten(l2))

结果:

1
2
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

这比underscore._.flatten快18倍:

1
2
3
Average time over 1000 trials of matplotlib.cbook.flatten: 2.55e-05 sec
Average time over 1000 trials of underscore._.flatten: 4.63e-04 sec
(time for underscore._)/(time for matplotlib.cbook) = 18.1233394636

你也可以使用NumPy's flat:

1
2
import numpy as np
list(np.array(l).flat)

编辑11/02/2016:只有当子列表具有相同的维度时才有效。


您可以使用numpy:flat_list = list(np.concatenate(list_of_list))


递归版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
x = [1,2,[3,4],[5,[6,[7]]],8,9,[10]]

def flatten_list(k):
    result = list()
    for i in k:
        if isinstance(i,list):

            #The isinstance() function checks if the object (first argument) is an
            #instance or subclass of classinfo class (second argument)

            result.extend(flatten_list(i)) #Recursive call
        else:
            result.append(i)
    return result

flatten_list(x)
#result = [1,2,3,4,5,6,7,8,9,10]

1
2
3
4
5
6
7
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])

简单的代码为underscore.py包风扇

1
2
3
from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

它解决了所有的平面问题(没有列表项或复杂嵌套)

1
2
3
4
5
from underscore import _
# 1 is none list item
# [2, [3]] is complex nesting
_.flatten([1, [2, [3]], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

您可以使用pip安装underscore.py

1
pip install underscore.py


如果你愿意为了更干净的外观而牺牲一点速度,那么你可以使用numpy.concatenate().tolist()numpy.concatenate().ravel().tolist():

1
2
3
4
5
6
7
8
9
10
11
12
import numpy

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] * 99

%timeit numpy.concatenate(l).ravel().tolist()
1000 loops, best of 3: 313 μs per loop

%timeit numpy.concatenate(l).tolist()
1000 loops, best of 3: 312 μs per loop

%timeit [item for sublist in l for item in sublist]
1000 loops, best of 3: 31.5 μs per loop

您可以在文档numpy中找到更多信息。连接和numpy.ravel


另一种不同寻常的方法适用于异质和齐次整数列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from typing import List


def flatten(l: list) -> List[int]:
   """Flatten an arbitrary deep nested list of lists of integers.

    Examples:
        >>> flatten([1, 2, [1, [10]]])
        [1, 2, 1, 10]

    Args:
        l: Union[l, Union[int, List[int]]

    Returns:
        Flatted list of integer
   """

    return [int(i.strip('[ ]')) for i in str(l).split(',')]


我找到的最快的解决方案(无论如何对于大列表):

1
2
3
import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()

完成了!你当然可以通过执行list(l)把它变成一个列表


1
2
3
flat_list = []
for i in list_of_list:
    flat_list+=i

这段代码也可以很好地工作,因为它只是一直扩展列表。虽然非常相似,但只有一个for循环。所以它的复杂度比添加2 for循环要小。


一个简单的递归方法使用reducefunctoolsadd运算符对列表:

1
2
3
4
5
>>> from functools import reduce
>>> from operator import add
>>> flatten = lambda lst: [lst] if type(lst) is int else reduce(add, [flatten(ele) for ele in lst])
>>> flatten(l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

函数flatten接受lst作为参数。它循环lst的所有元素,直到到达整数(对于其他数据类型,还可以将int更改为floatstr等),这些整数被添加到最外层递归的返回值中。

递归不同于for循环和单子之类的方法,它是一种不受列表深度限制的通用解决方案。例如,深度为5的列表可以像l一样被压平:

1
2
3
>>> l2 = [[3, [1, 2], [[[6], 5], 4, 0], 7, [[8]], [9, 10]]]
>>> flatten(l2)
[3, 1, 2, 6, 5, 4, 0, 7, 8, 9, 10]

这可能不是最有效的方法,但我想放一行(实际上是两行)。两个版本都可以在任意层次嵌套列表上工作,并利用语言特性(Python3.5)和递归。

1
2
3
4
5
6
7
8
def make_list_flat (l):
    flist = []
    flist.extend ([l]) if (type (l) is not list) else [flist.extend (make_list_flat (e)) for e in l]
    return flist

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = make_list_flat(a)
print (flist)

输出是

1
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

这是以深度优先的方式工作的。递归一直向下,直到找到一个非列表元素,然后扩展局部变量flist,然后回滚到父元素。无论何时返回flist,它都会扩展到列表理解中的父类flist。因此,在根目录中,返回一个平面列表。

上面的一个创建了几个本地列表并返回它们,这些列表用于扩展父列表。我认为解决这个问题的方法可能是创建一个gloabl flist,如下所示。

1
2
3
4
5
6
7
a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = []
def make_list_flat (l):
    flist.extend ([l]) if (type (l) is not list) else [make_list_flat (e) for e in l]

make_list_flat(a)
print (flist)

输出也是

1
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

虽然我不确定这个时候的效率。


注意:下面的代码适用于Python 3.3+,因为它使用yield_fromsix也是一个第三方包,虽然它是稳定的。或者,您可以使用sys.version

对于obj = [[1, 2,], [3, 4], [5, 6]],这里的所有解决方案都很好,包括列表理解和itertools.chain.from_iterable

然而,考虑一下这个稍微复杂一点的情况:

1
>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]

这里有几个问题:

一个元素,6,只是一个标量;它是不可迭代的,所以上面的路由在这里会失败。一个元素'abc'在技术上是可迭代的(所有的str都是)。但是,从字里行间可以看出,您并不想这样对待它——您想将它视为单个元素。最后一个元素[8, [9, 10]]本身是一个嵌套迭代器。基本列表理解和chain.from_iterable只提取"向下一级"。

你可以补救如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> from collections import Iterable
>>> from six import string_types

>>> def flatten(obj):
...     for i in obj:
...         if isinstance(i, Iterable) and not isinstance(i, string_types):
...             yield from flatten(i)
...         else:
...             yield i


>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]

在这里,您检查子元素(1)是否可以使用Iterable进行迭代,这是来自itertools的ABC,但是还需要确保(2)元素不是"string-like"。


最近,我遇到了一种情况,在子列表中混合了字符串和数字数据,比如

1
2
3
4
5
6
7
8
9
10
11
test = ['591212948',
['special', 'assoc', 'of', 'Chicago', 'Jon', 'Doe'],
['Jon'],
['Doe'],
['fl'],
92001,
555555555,
'hello',
['hello2', 'a'],
'b',
['hello33', ['z', 'w'], 'b']]

flat_list = [item for sublist in test for item in sublist]这样的方法不起作用的地方。因此,对于1+级的子列表,我提出了以下解决方案

1
2
3
4
5
6
7
8
9
def concatList(data):
    results = []
    for rec in data:
        if type(rec) == list:
            results += rec
            results = concatList(results)
        else:
            results.append(rec)
    return results

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
In [38]: concatList(test)
Out[38]:
 Out[60]:
['591212948',
'special',
'assoc',
'of',
'Chicago',
'Jon',
'Doe',
'Jon',
'Doe',
'fl',
92001,
555555555,
'hello',
'hello2',
'a',
'b',
'hello33',
'z',
'w',
'b']

使用实际堆栈数据结构可以非常简单地避免对堆栈的递归调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
alist = [1,[1,2],[1,2,[4,5,6],3,"33"]]
newlist = []

while len(alist) > 0 :
  templist = alist.pop()
  if type(templist) == type(list()) :
    while len(templist) > 0 :
      temp = templist.pop()
      if type(temp) == type(list()) :
        for x in temp :
          templist.append(x)
      else :
        newlist.append(temp)
  else :
    newlist.append(templist)
print(list(reversed(newlist)))


我们可以使用python的基本概念来做同样的事情

1
2
3
4
5
6
7
8
9
10
11
nested_list=[10,20,[30,40,[50]],[80,[10,[20]],90],60]
flat_list=[]
def unpack(list1):
    for item in list1:
        try:
            len(item)
            unpack(item)
        except:
            flat_list.append(item)
unpack(nested_list)
print (flat_list)

清理了@Deleet示例

1
2
3
4
5
6
7
8
9
10
11
12
13
from collections import Iterable

def flatten(l, a=[]):
    for i in l:
        if isinstance(i, Iterable):
            flatten(i, a)
        else:
            a.append(i)
    return a

daList = [[1,4],[5,6],[23,22,234,2],[2], [ [[1,2],[1,2]],[[11,2],[11,22]] ] ]

print(flatten(daList))

例如:https://repl.it/G8mb/0


这可以使用toolz.concatcytoolz.concat (cythonized version,在某些情况下可能更快):

1
2
3
from cytoolz import concat
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(concat(l)) # or just `concat(l)` if one only wants to iterate over the items

在我的电脑上,在python 3.6中,这个时间看起来几乎和[item for sublist in l for item in sublist]一样快(不包括导入时间):

1
2
3
4
5
6
7
8
9
10
11
In [611]: %timeit L = [item for sublist in l for item in sublist]
695 ns ± 2.75 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [612]: %timeit L = [item for sublist in l for item in sublist]
701 ns ± 5.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [613]: %timeit L = list(concat(l))
719 ns ± 12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [614]: %timeit L = list(concat(l))
719 ns ± 22.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

toolz版本确实较慢:

1
2
3
4
5
6
7
In [618]: from toolz import concat

In [619]: %timeit L = list(concat(l))
845 ns ± 29 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [620]: %timeit L = list(concat(l))
833 ns ± 8.73 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

您可以使用chain类

chain.from_iterable(['ABC', 'DEF'])

输出将是:[A B C D E F]

参考:https://docs.python.org/2/library/itertools.html # itertools.chain


这适用于嵌套列表。它可以很容易地扩展到使用其他类型的迭代器。

1
2
3
4
5
6
7
8
9
def flatten(seq):
   """list -> list                                                                                                                                                                          
    return a flattend list from an abitrarily nested list                                                                                                                                    
   """

    if not seq:
        return seq
    if not isinstance(seq[0], list):
        return [seq[0]] + flatten(seq[1:])
    return flatten(seq[0]) + flatten(seq[1:])

样本运行

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

使用列表理解:

1
mylist = [item for sublist in l for item in sublist]


我认为最简单和通用的方法是这样写递归函数:

1
2
3
4
5
6
7
8
9
10
11
def flat_list(some_list = []):
elements=[]
for item in some_list:
    if type(item) == type([]):
        elements += flat_list(item)
    else:
        elements.append(item)
return elements

list =  ['a', 'b', 1, 2, 3, [1, 2, 3, 'c',[112,123,111,[1234,1111,3333,44444]]]]
flat_list(list)

>>> ['a', 'b', 1, 2, 3, 1, 2, 3, 'c', 112, 123, 111, 1234, 1111, 3333, 44444]

吗?