如何从列表列表中制作一个简单列表?

How do I make a flat list out of a list of lists?

提问人:Emma 提问时间:6/5/2009 最后编辑:buhtzEmma 更新时间:11/7/2023 访问量:4156786

问:

我有一个列表列表,例如

[
    [1, 2, 3],
    [4, 5, 6],
    [7],
    [8, 9]
]

我怎样才能把它压平才能得到?[1, 2, 3, 4, 5, 6, 7, 8, 9]


如果你的列表列表来自嵌套列表推导,那么通过修复推导,可以更简单/直接地解决问题;请参阅如何从列表推导式而不是嵌套列表中获得平面结果?

这里最流行的解决方案通常只展平嵌套列表的一个“级别”。请参阅展平不规则(任意嵌套)列表列表,了解完全展平深度嵌套结构的解决方案(通常以递归方式)。

python 列表 多维数组 展平

评论

57赞 RichieHindle 6/5/2009
这里有一个关于这个问题的深入讨论:rightfootin.blogspot.com/2006/09/more-on-python-flatten.html,讨论了几种扁平化任意嵌套列表的方法。有趣的读物!
3赞 Golden Lion 12/19/2020
stackoverflow.com/questions/50259290/......(本文解释了 np.flatten() 和 tf.flatten() 使用(静态与动态)ndarray 之间的区别。

答:

333赞 Greg Hewgill 6/5/2009 #1

使用 functools.reduce,将累积列表添加到下一个列表:xsys

from functools import reduce
xss = [[1,2,3], [4,5,6], [7], [8,9]]
out = reduce(lambda xs, ys: xs + ys, xss)

输出:

[1, 2, 3, 4, 5, 6, 7, 8, 9]

使用 operator.concat 的更快方法:

from functools import reduce
import operator
xss = [[1,2,3], [4,5,6], [7], [8,9]]
out = reduce(operator.concat, xss)

输出:

[1, 2, 3, 4, 5, 6, 7, 8, 9]

评论

12赞 Mike Graham 4/26/2012
这是 Shlemiel 画家的算法 joelonsoftware.com/articles/fog0000000319.html
1赞 gntskn 10/3/2020
reduce对于此用例来说,效率非常低,因为它将重复副本并生成许多未使用的临时列表(O(n^2),无论是在时间上还是在空间上,具体取决于 GC 决定如何清理)。最好使用 append 或 extend。
0赞 dark_ruby 4/26/2023
如果内部列表或外部列表为空,则此操作失败
1369赞 Kenan Banks 6/5/2009 #2

作者注:这是非常低效的。但很有趣,因为单体很棒。

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

sum对可迭代对象的元素求和,并使用第二个参数作为求和的初始值。(默认初始值为 ,它不是列表。xss[]0

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

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

评论

162赞 andrewrk 6/16/2010
这很整洁和聪明,但我不会使用它,因为它读起来很混乱。
115赞 Mike Graham 4/26/2012
这是画家施莱米尔的算法 joelonsoftware.com/articles/fog0000000319.html——不必要的低效率,也不必要的丑陋。
68赞 ulidtko 12/3/2014
列表上的追加运算形成了一个 Monoid,它是在一般意义上思考运算的最方便的抽象之一(不仅限于数字)。所以这个答案应该得到我的+1,因为我(正确地)将列表视为幺半群。不过,性能令人担忧......+
22赞 Jean-François Fabre 8/1/2017
这是一种非常低效的方法,因为总和的二次方位。
13赞 ds4940 1/5/2018
本文解释了低效率 mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python 的数学原理
7255赞 Alex Martelli 6/5/2009 #3

可以使用列表推导式对命名列表进行扁平化: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

解释:当有 L 个子列表时,基于的方法(包括 中的隐含用法)是必要的——随着中间结果列表越来越长,在每一步都会分配一个新的中间结果列表对象,并且必须复制上一个中间结果中的所有项目(以及在末尾添加的一些新项目)。因此,为了简单起见,并且不损失实际的通用性,假设您有 L 个子列表,每个子列表包含 M 个项目:前 M 个项目来回复制次数,第二个 M 项目来回复制,依此类推;总份数是 M 乘以 x 从 1 到 L 的 x 之和,即 .+sumO(L**2)L-1L-2M * (L**2)/2

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

评论

651赞 intuited 10/15/2010
我尝试使用相同的数据进行测试,使用: 。它的运行速度是嵌套列表推导的两倍多一点,嵌套列表推导是此处显示的替代方案中最快的。itertools.chain.from_iterable$ python -mtimeit -s'from itertools import chain; l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'list(chain.from_iterable(l))'
366赞 Rob Crowell 7/28/2011
我发现语法很难理解,直到我意识到你可以把它想象成嵌套的for循环。对于 L 中的子列表:对于子列表中的项目:生成项目
264赞 John Mee 8/29/2013
[树叶在森林中树叶]可能更容易理解和应用。
53赞 Sнаđошƒаӽ 7/13/2021
@RobCrowell 这里也一样。对我来说,列表理解读不对,感觉有些不对劲——我似乎总是弄错了,最终在谷歌上搜索。对我来说,这读起来是对的.我希望是这样。我敢肯定我在这里遗漏了一些关于语法的东西,如果有人能指出这一点,我将不胜感激。[leaf for leaf in tree for tree in forest]
85赞 Gilthans 8/11/2021
每次我想扁平化列表时,我都会一直在这里寻找,但这个 gif 是驱使它回家的原因:i.stack.imgur.com/0GoV5.gif
36赞 Igor Krivokon 6/5/2009 #4

函数不起作用的原因是扩展就地扩展了数组并且不返回它。您仍然可以使用 lambda 返回 x,如下所示:

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

注意:在列表中,extend 比 + 更有效。

评论

9赞 agf 9/24/2011
extend最好用作 ,,因为它避免了 、 和 上的属性查找的(相当大的)开销。newlist = []extend = newlist.extendfor sublist in l: extend(l)lambdaxor
0赞 Markus Dutschke 7/2/2019
对于 Python 3,添加from functools import reduce
2336赞 Shawn Chin 6/5/2009 #5

你可以使用 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

评论

27赞 Tim Dierks 9/3/2014
这是一件棘手的事情,它比列表理解更简单。你必须知道,链只将作为参数传递的可迭代对象连接在一起,而 * 会导致顶级列表扩展为参数,因此将所有这些可迭代对象连接在一起,但不会进一步下降。我认为这使得理解比在这种情况下使用链更具可读性。*chainchain
144赞 ShadowRanger 11/13/2015
@TimDierks:我不确定“这需要你理解 Python 语法”是否反对在 Python 中使用给定技术。当然,复杂的用法可能会造成混淆,但“splat”运算符在许多情况下通常很有用,这并不是以一种特别晦涩的方式使用它;拒绝所有对初学者来说不一定显而易见的语言功能意味着你把一只手绑在背后。不妨在你做的时候也抛出列表理解;来自其他背景的用户会发现一个循环反复出现更明显的循环。forappend
4赞 nadapez 10/21/2021
* 创建一个中间元组。! 直接从顶部列表获取嵌套列表。from_iterable
2赞 Chris Collett 10/25/2021
为了使它更具可读性,您可以创建一个简单的函数:.类型提示提高了正在发生的事情的清晰度(现代 IDE 会将其解释为返回类型)。def flatten_list(deep_list: list[list[object]]):return list(chain.from_iterable(deep_list))list[object]
2赞 Lukas Juhrich 3/13/2023
更短的是python3.5+,2015 年发布)[*chain(*l)]
22赞 mdh 7/17/2016 #6

也可以使用 NumPy 的 flat

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

仅当子列表具有相同的维度时,它才有效。

8赞 mkultra 10/27/2016 #7

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

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.concatenate 和 numpy.ravel 中找到更多信息。

评论

2赞 EL_DON 4/23/2019
不适用于不均匀嵌套的列表,例如[1, 2, [3], [[4]], [5, [6]]]
0赞 EL_DON 8/3/2021
@juanpa.arrivillaga 不过,这是问题的简单而自然的延伸。能够处理更深度嵌套的答案更有可能对发现此问题的人有用。
120赞 MSeifert 11/26/2016 #8

若要展平深度嵌套的数据结构,请使用 iteration_utilities.deepflatten1

>>> 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的薄包装器:

>>> 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

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

结果表明,如果可迭代对象只包含几个内部可迭代对象,那么它将是最快的,但是对于长迭代对象,只有 或嵌套理解具有合理的性能,并且是最快的(正如 Nico Schlömer 已经注意到的那样)。sumitertools.chain.from_iterableiteration_utilities.deepflattenitertools.chain.from_iterable

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()

1 免责声明:我是该库的作者

196赞 pylang 11/29/2016 #9

下面是适用于数字字符串嵌套列表和混合容器的一般方法。这可以使简单和复杂的容器扁平化(另请参阅演示)。

法典

from typing import Iterable 
#from collections import Iterable                            # < py38


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
  • 在 Python 3.8 中,抽象基类从模块移动到模块。collection.abctyping

演示

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

complicated = [[1, [2]], (3, 4, {5, 6}, 7), 8, "9"]              # numbers, strs, nested & mixed
list(flatten(complicated))
# [1, 2, 3, 4, 5, 6, 7, 8, '9']

参考

  • 该解决方案是根据 Beazley, D. 和 B. Jones 的配方修改而来的。Recipe 4.14,Python Cookbook 第 3 版,O'Reilly Media Inc.,加利福尼亚州塞瓦斯托波尔:2013 年。
  • 找到了一个早期的 SO 帖子,可能是原始演示。

评论

8赞 Martin Thoma 3/25/2017
我只是写了差不多,因为我没有看到你的解决方案......这是我寻找的“递归展平完整的多个列表”......(+1)
5赞 pylang 3/26/2017
@MartinThoma 非常感谢。仅供参考,如果扁平化嵌套可迭代对象对您来说是一种常见的做法,那么有一些第三方包可以很好地处理这个问题。这样可以避免重新发明轮子。我已经提到了这篇文章中讨论的其他内容。 干杯。more_itertools
0赞 Wolf 6/15/2017
也许也可以成为这种树的好名字,而我会通过坚持嵌套列表来降低这个答案的通用性。traverse
0赞 Ryan Allen 5/1/2018
您可以检查而不是导入/检查,这也将排除字符串。if hasattr(x, '__iter__')Iterable
0赞 sunnyX 6/13/2019
如果其中一个嵌套列表具有字符串列表,则上面的代码似乎不起作用。[1, 2, [3, 4], [4], [], 9, 9.5, 'ssssss', ['str', 'sss', 'ss'], [3, 4, 5]] 输出:- [1, 2, 3, 4, 4, 9, 9.5, 'ssssss', 3, 4, 5]
50赞 pylang 12/3/2016 #10

请考虑安装 more_itertools 软件包。

> pip install more_itertools

它附带了一个扁平化的实现(来源,来自 itertools 配方):

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 提供)。

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]

评论

0赞 viddik13 3/5/2020
如果你有能力向你的项目添加一个包 - 这个答案是最好的
0赞 Sajad.sni 9/8/2020
当所有元素都未列出时,它将失败。(例如 lst=[1, [2,3]])。当然,整数是不可迭代的。
1赞 viddik13 10/30/2020
另外,请注意,字符串列表将被扁平化为字符列表
1赞 mirekphd 4/20/2023
我会颠倒答案,强调(把它留给纯列表列表的特殊情况)。collapseflatten
53赞 devil in the detail 7/5/2017 #11

以下对我来说似乎最简单:

>>> 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]

评论

4赞 kylebebak 10/3/2021
OP 没有提到他们想使用 numpy。Python 有很好的方法可以做到这一点,而无需依赖库
886赞 Nico Schlömer 7/26/2017 #12

我用perfplot(我的一个宠物项目,本质上是一个包装器)测试了大多数建议的解决方案,发现timeit

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

成为最快的解决方案,无论是当许多小列表还是几个长列表连接在一起时。(同样快。operator.iadd

一个更简单且也可以接受的变体是

out = []
for sublist in a:
    out.extend(sublist)

如果子列表的数量很大,则其性能会比上述建议稍差。

enter image description here

enter image description here


重现绘图的代码:

import functools
import itertools
import operator

import numpy as np
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(np.array(a).flat)


def numpy_concatenate(a):
    return list(np.concatenate(a))


def extend(a):
    out = []
    for sublist in a:
        out.extend(sublist)
    return out


b = perfplot.bench(
    setup=lambda n: [list(range(10))] * n,
    # setup=lambda n: [list(range(n))] * 10,
    kernels=[
        forfor,
        sum_brackets,
        functools_reduce,
        functools_reduce_iconcat,
        itertools_chain,
        numpy_flat,
        numpy_concatenate,
        extend,
    ],
    n_range=[2 ** k for k in range(16)],
    xlabel="num lists (of length 10)",
    # xlabel="len lists (10 lists total)"
)
b.save("out.png")
b.show()

评论

57赞 Sara 1/20/2019
对于大型嵌套列表,list(numpy.array(a).flat)' 是上述所有函数中最快的。
2赞 Leo 4/30/2020
有没有办法做一个3-D性能图?按平均数组大小计算的数组数量?
3赞 11/14/2020
你能@Sara定义“巨大”吗?
6赞 mirekphd 12/5/2020
尝试了 Rossetta Code(链接)中的测试示例并得到了numpy_flatVisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
4赞 Chau Pham 1/16/2022
对于不同的子列表长度,np.array 不起作用。例如,将返回原始列表。使用更安全flata = [ [1,2], [1,2,3]]list(np.array(a).flat)list(np.concatenate(a))
5赞 englealuze 8/8/2017 #13
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])

评论

0赞 EL_DON 4/23/2019
对于问题中的示例嵌套列表,python2.7 失败:[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
2赞 tharndt 1/9/2018 #14

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

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(',')]

评论

0赞 Darkonaut 1/11/2018
这只是 ᴡʜᴀᴄᴋᴀᴍᴀᴅᴏᴅʟᴇ3000 之前已经发布的内容的一种更复杂、更慢的方式。我昨天重新发明了他的提议,所以这种方法现在似乎很流行;)
0赞 tharndt 1/11/2018
差一点:wierd_list = [[1, 2, 3], [4, 5, 6], [7], [8, 9], 10] >> nice_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0]
0赞 tharndt 1/11/2018
我的代码作为一个衬里是:flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')]
1赞 Darkonaut 1/12/2018
你确实是对的 +1,ᴡʜᴀᴄᴋᴀᴍᴀᴅᴏᴅʟᴇ3000 的提议不适用于多位数,我之前也没有测试过,尽管应该很明显。您可以简化代码并编写 .但我建议在实际用例中坚持 Deleet 的建议。它不包含 hacky 类型转换,它更快、更通用,因为它自然也处理混合类型的列表。[int(e.strip('[ ]')) for e in str(deep_list).split(',')]
2赞 tharndt 1/15/2018
很抱歉,不可以。但是我最近在这里看到了这段代码: Python 练习册 6.1.2
29赞 EL_DON 2/2/2018 #15

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

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

这比 underscore._.flatten 快 18 倍:

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
10赞 Brad Solomon 2/2/2018 #16

注意:以下内容适用于 Python 3.3+,因为它使用 yield_from。 虽然很稳定,但它也是第三方软件包。或者,您可以使用 .sixsys.version


在 的情况下,这里的所有解决方案都很好,包括列表推导和 。obj = [[1, 2,], [3, 4], [5, 6]]itertools.chain.from_iterable

但是,请考虑以下稍微复杂的情况:

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

这里有几个问题:

  • 一个元素 ,只是一个标量;它是不可迭代的,所以上面的路由在这里会失败。6
  • 一个元素 , 在技术上可迭代的(所有元素都是可迭代的)。然而,在字里行间阅读一下,你不想这样对待它——你想把它当作一个单一的元素。'abc'str
  • 最后一个元素本身就是一个嵌套的可迭代对象。基本列表理解,仅提取“1 级”。[8, [9, 10]]chain.from_iterable

您可以按如下方式解决此问题:

>>> 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(来自 的 ABC)进行迭代,但也要确保 (2) 该元素不是“类字符串”。itertools

评论

1赞 pylang 6/20/2018
如果您仍然对 Python 2 兼容性感兴趣,请更改为循环,例如yield fromforfor x in flatten(i): yield x
4赞 phoxis 5/16/2018 #17

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

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, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

这首先以深度的方式工作。递归向下,直到找到一个非列表元素,然后扩展局部变量,然后将其回滚到父级。每当返回时,它都会扩展到列表推导式中的父项。因此,在根目录处,将返回一个简单列表。flistflistflist

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

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, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

虽然我现在不确定效率。

评论

2赞 Maciek 4/10/2020
为什么 extend([l]) 而不是 append(l)?
14赞 kederrac 1/26/2020 #18

您可以使用该方法。它显示是最快的:listextend

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

性能:

import functools
import itertools
import numpy
import operator
import perfplot


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 extend(a):
    n = []

    list(map(n.extend, a))

    return n


perfplot.show(
    setup = lambda n: [list(range(10))] * n,
    kernels = [
        functools_reduce_iconcat, extend, itertools_chain, numpy_flat
        ],
    n_range = [2**k for k in range(16)],
    xlabel = 'num lists',
    )

输出:

Enter image description here

12赞 mmj 8/8/2020 #19

有几个答案使用与下面相同的递归追加方案,但没有一个使用 ,这使得解决方案更加健壮和 Pythonictry

def flatten(itr):
    for x in itr:
        try:
            yield from flatten(x)
        except TypeError:
            yield x

用法:这是一个生成器,你通常希望将其包含在可迭代的构建器中,例如 OR,或者在循环中使用它。list()tuple()for

该解决方案的优点是:

  • 适用于任何类型的可迭代(甚至是未来的可迭代!
  • 适用于任何组合和深度的嵌套
  • 如果顶层包含裸项,也有效
  • 无依赖关系
  • 快速高效(您可以部分展平嵌套的可迭代对象,而不会在不需要的剩余部分上浪费时间)
  • 通用(你可以用它来构建你选择的迭代对象或循环中)

注意:由于所有可迭代对象都是扁平化的,因此字符串被分解为单个字符的序列。如果你不喜欢/不想这样的行为,你可以使用以下版本,它从扁平化可迭代对象(如字符串和字节)中过滤出来:

def flatten(itr):
    if type(itr) in (str,bytes):
        yield itr
    else:
        for x in itr:
            try:
                yield from flatten(x)
            except TypeError:
                yield x

评论

0赞 Vladimir Vilimaitis 3/6/2023
如果类型的元组是哈希集,那不是稍微快一点吗?
0赞 mmj 3/7/2023
@VladimirVilimaitis 如回答所述,代码返回一个生成器,您可以直接使用它,也可以使用它创建一个类型与扁平化过程无关的序列,这取决于您的需要。
4赞 Alon Gouldman 10/8/2021 #20

我想要一个可以处理多个嵌套的解决方案(例如),但也不是递归的(我有一个很大的递归级别,我得到了一个递归错误。[[1], [[[2]], [3]]], [1, 2, 3]

这是我想出的:

def _flatten(l) -> Iterator[Any]:
    stack = l.copy()
    while stack:
        item = stack.pop()
        if isinstance(item, list):
            stack.extend(item)
        else:
            yield item


def flatten(l) -> Iterator[Any]:
    return reversed(list(_flatten(l)))

和测试:

@pytest.mark.parametrize('input_list, expected_output', [
    ([1, 2, 3], [1, 2, 3]),
    ([[1], 2, 3], [1, 2, 3]),
    ([[1], [2], 3], [1, 2, 3]),
    ([[1], [2], [3]], [1, 2, 3]),
    ([[1], [[2]], [3]], [1, 2, 3]),
    ([[1], [[[2]], [3]]], [1, 2, 3]),
])
def test_flatten(input_list, expected_output):
    assert list(flatten(input_list)) == expected_output
1赞 user7864386 12/9/2021 #21

一个非递归函数,用于展平任何深度的列表列表:

def flatten_list(list1):
    out = []
    inside = list1
    while inside:
        x = inside.pop(0)
        if isinstance(x, list):
            inside[0:0] = x
        else:
            out.append(x)
    return out

l = [[[1,2],3,[4,[[5,6],7],[8]]],[9,10,11]]
flatten_list(l)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
34赞 dtlam26 12/9/2021 #22

根据您的列表,即 1 个列表级别,我们可以简单地使用而无需使用任何库[[1, 2, 3], [4, 5, 6], [7], [8, 9]]sum(list,[])

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

当内部存在元组或数字时,扩展此方法的优点。只需将每个元素的映射函数添加到列表中即可map

#For only tuple
sum(list(map(list,[[1, 2, 3], (4, 5, 6), (7,), [8, 9]])),[])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

#In general

def convert(x):
    if type(x) is int or type(x) is float:
           return [x]
    else:
           return list(x)

sum(list(map(convert,[[1, 2, 3], (4, 5, 6), 7, [8, 9]])),[])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

在这里,对这种方法在内存方面的缺点进行了明确的解释。简而言之,它以递归方式创建列表对象,应避免:(

评论

5赞 Uriya Harpeness 12/9/2021
这个问题已经给出了这个答案:stackoverflow.com/a/952946/14273548
3赞 Arel 12/30/2021
整洁!尽管这里的另一个答案,stackoverflow.com/a/952946/14273548,解释了通常应该避免这种解决方案的原因(它效率低下且令人困惑)。
2赞 Culpepper 2/1/2022
如果列表包含元组,也会给出 TypeError
0赞 S.B 1/11/2022 #23

如果我想在前面的答案中添加一些东西,这是我的递归函数,它不仅可以展平嵌套列表,还可以展平任何给定的容器或任何可以抛出项目的对象。这也适用于任何深度的嵌套,它是一个惰性迭代器,可根据要求生成项目:flatten

def flatten(iterable):
    # These types won't considered a sequence or generally a container
    exclude = str, bytes

    for i in iterable:
        try:
            if isinstance(i, exclude):
                raise TypeError
            iter(i)
        except TypeError:
            yield i
        else:
            yield from flatten(i)

这样,您可以排除不想扁平化的类型,例如或其他类型。str

这个想法是,如果一个对象可以通过它准备好产生物品。因此,可迭代对象甚至可以将生成器表达式作为项。iter()

有人可能会争辩说:为什么当 OP 没有要求它时,你把这个写得那么通用?好的,你是对的。我只是觉得这可能会帮助某人(就像它对我自己所做的那样)。

测试用例:

lst1 = [1, {3}, (1, 6), [[3, 8]], [[[5]]], 9, ((((2,),),),)]
lst2 = ['3', B'A', [[[(i ** 2 for i in range(3))]]], range(3)]

print(list(flatten(lst1)))
print(list(flatten(lst2)))

输出:

[1, 3, 1, 6, 3, 8, 5, 9, 2]
['3', b'A', 0, 1, 4, 0, 1, 2]
2赞 Vincent Aranega 3/14/2022 #24

不是单行,但看到这里的所有答案,我想这个长长的列表错过了一些模式匹配,所以这里是:)

这两种方法可能效率不高,但无论如何,它很容易阅读(至少对我来说是这样;也许我被函数式编程宠坏了):

def flat(x):
    match x:
        case []:
            return []
        case [[*sublist], *r]:
            return [*sublist, *flat(r)]

第二个版本考虑了列表的列表列表...无论嵌套方式如何:

def flat(x):
    match x:
        case []:
            return []
        case [[*sublist], *r]:
            return [*flat(sublist), *flat(r)]
        case [h, *r]:
            return [h, *flat(r)]
-2赞 BhushanD 4/2/2022 #25

考虑到列表只有整数:

import re
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(map(int,re.sub('(\[|\])','',str(l)).split(',')))
-3赞 Jayesh Chandrapal 4/30/2022 #26
def flatten_array(arr):
  result = []
  for item in arr:
    if isinstance(item, list):
      for num in item:
        result.append(num)
    else:
      result.append(item)
  return result

print(flatten_array([1, 2, [3, 4, 5], 6, [7, 8], 9]))
// output: [1, 2, 3, 4, 5, 6, 7, 8, 9]
0赞 Vova 5/16/2022 #27

我建议使用带有 yield 语句和 yield from 的生成器。 下面是一个示例:

from collections.abc import Iterable

def flatten(items, ignore_types=(bytes, str)):
    """
       Flatten all of the nested lists to the one. Ignoring flatting of iterable types str and bytes by default.
    """
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, ignore_types):
            yield from flatten(x)
        else:
            yield x

values = [7, [4, 3, 5, [7, 3], (3, 4), ('A', {'B', 'C'})]]

for v in flatten(values):
    print(v)
2赞 Letsios Matthaios 7/25/2022 #28

如果你想取消嵌套所有内容并保留一个不同的元素列表,你也可以使用它。

list_of_lists = [[1,2], [2,3], [3,4]]
list(set.union(*[set(s) for s in list_of_lists]))
0赞 X0-user-0X 8/10/2022 #29

对于包含多个列表的列表,这里有一个对我有用并且我希望是正确的递归解决方案:

# Question 4
def flatten(input_ls=[]) -> []:
    res_ls = []
    res_ls = flatten_recursive(input_ls, res_ls)

    print("Final flatten list solution is: \n", res_ls)

    return res_ls


def flatten_recursive(input_ls=[], res_ls=[]) -> []:
    tmp_ls = []

    for i in input_ls:
        if isinstance(i, int):
            res_ls.append(i)
        else:
            tmp_ls = i
            tmp_ls.append(flatten_recursive(i, res_ls))

    print(res_ls)
    return res_ls


flatten([0, 1, [2, 3], 4, [5, 6]])  # test
flatten([0, [[[1]]], [[2, 3], [4, [[5, 6]]]]])

输出:

[0, 1, 2, 3]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
Final flatten list solution is: 
 [0, 1, 2, 3, 4, 5, 6]
[0, 1]
[0, 1]
[0, 1]
[0, 1, 2, 3]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
Final flatten list solution is: 
 [0, 1, 2, 3, 4, 5, 6]
-6赞 Hans 9/2/2022 #30

我创建了一个小函数,它基本上可以展平任何东西。 你可以用 pip 得到它: pip install flatten-everything

from flatten_everything import flatten_everything
withoutprotection=list(
    flatten_everything(
        [
            1,
            1,
            2,
            [3, 4, 5, [6, 3, [2, 5, ["sfs", "sdfsfdsf",]]]],
            1,
            3,
            34,
            [
                55,
                {"brand": "Ford", "model": "Mustang", "year": 1964, "yearxx": 2020},
                pd.DataFrame({"col1": [1, 2], "col2": [3, 4]}),
                {"col1": [1, 2], "col2": [3, 4]},
                55,
                {"k32", 34},
                np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]),
                (np.arange(22), np.eye(2, 2), 33),
            ],
        ]
    )
)
print(withoutprotection)
output:
[1, 1, 2, 3, 4, 5, 6, 3, 2, 5, 'sfs', 'sdfsfdsf', 1, 3, 34, 55, 'Ford', 'Mustang', 1964, 2020, 1, 2, 3, 4, 1, 2, 3, 4, 55, 34, 'k32', 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 1.0, 0.0, 0.0, 1.0, 33]

您甚至可以保护对象不被展平:

from flatten_everything import ProtectedDict,ProtectedList,ProtectedTuple
withprotection=list(
    flatten_everything(
        [
            1,
            1,
            2,
            [3, 4, 5, [6, 3, [2, 5, ProtectedList(["sfs", "sdfsfdsf",])]]],
            1,
            3,
            34,
            [
                55,
                ProtectedDict({"brand": "Ford", "model": "Mustang", "year": 1964, "yearxx": 2020}),
                pd.DataFrame({"col1": [1, 2], "col2": [3, 4]}),
                {"col1": [1, 2], "col2": [3, 4]},
                55,
                {"k32", 34},
                np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]),
                ProtectedTuple((np.arange(22), np.eye(2, 2), 33)),
            ],
        ]
    )
)
print(withprotection)
output:
[1, 1, 2, 3, 4, 5, 6, 3, 2, 5, ['sfs', 'sdfsfdsf'], 1, 3, 34, 55, {'brand': 'Ford', 'model': 'Mustang', 'year': 1964, 'yearxx': 2020}, 1, 2, 3, 4, 1, 2, 3, 4, 55, 34, 'k32', 1, 2, 3, 4, 5, 6, 7, 8, (array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,17, 18, 19, 20, 21]), array([[1., 0.], [0., 1.]]), 33)]
1赞 Vasantha Ganesh 10/14/2022 #31

如果你有一个 numpy 数组:a

a = np.array([[1,2], [3,4]])
a.flatten('C')

生产:

[1, 2, 3, 4]

np.flatten还接受其他参数:

  • C:
  • F
  • A
  • K

有关参数的更多详细信息,请参阅此处

评论

3赞 Alexander 4/20/2023
此答案仅适用于矩形数组(矩阵)结构,不适用于包含列表的长度不相等的情况。
0赞 B.R. 3/12/2023 #32

我喜欢添加一个高性能的生成器解决方案,它可以增加任何深度的嵌套列表(或任何类型的可迭代列表),而不是(仅限 2D 列表):

from itertools import chain

def flatten_deep_generator(iterable):
    iterator = iter(iterable)
    try:
        while 1: # StopIteration will break the loop
            item = next(iterator)
            # check if item contains sub-items
            if not hasattr(item,'__trunc__'):
                iterator = chain(iter(item), iterator)
            else:
                yield item
    except StopIteration:
        pass

根据您的需要,生成器比列表具有巨大的优势。例如,如果您想在之后添加功能。只有在构建完整生成器(包括过滤)之后,才应在最后实例化生成的列表,从而避免对项目进行多次迭代。filter()

备注:与其他建议的生成器解决方案配对,这是一个迭代解决方案,而不是递归解决方案,它避免了在深度嵌套可迭代对象的情况下出现 RecursionErrors。

评论

0赞 Nik O'Lai 6/27/2023
在这种“高性能发电机解决方案”中的作用是什么?以及为什么__trunc__
0赞 B.R. 6/29/2023
该属性用于标识数字项,或者换句话说,不再可迭代的项。可能是我们有一些未涵盖的极端情况,那么您必须切换到更慢但更安全的方式:该解决方案应该适用于任何可迭代对象,因此我们不能使用像 .例如,该解决方案适用于嵌套迭代器、元组,甚至字符串也会扁平化为单个字符。__trunc__hastattr(item,'__iter__') or hasattr(item,'__next__')type(item) is list
1赞 Alain T. 3/22/2023 #33

这是我在其他答案中没有看到的方法。它支持任何级别的嵌套,迭代工作,无需库:

mylist = [[1,2,4,5],[[0,8,9],5,7],[3,11,[44,45,46],25]]

for i,_ in enumerate(mylist):          # indexes, including extended positions
    while isinstance(mylist[i],list):  # drill down/extend current position
        mylist[i:i+1] = mylist[i]      # as long as item is a list

print(mylist)
[1, 2, 4, 5, 0, 8, 9, 5, 7, 3, 11, 44, 45, 46, 25]

评论

0赞 Ry- 3/22/2023
不使用堆栈很好,但它的时间复杂度非常糟糕。