如何从列表列表中制作平面列表?


3364

我想知道是否有捷径可以从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'

20
这里对此进行了深入的讨论:rightfootin.blogspot.com/2006/09/more-on-python-flatten.html,讨论了将任意嵌套的列表列表扁平化的几种方法。有趣的读物!
RichieHindle

6
其他一些答案更好,但是您失败的原因是'extend'方法始终返回None。对于长度为2的列表,它将起作用,但返回None。对于更长的列表,它将消耗前2个arg,返回无。然后,继续执行None.extend(<third arg>),这将导致此错误
mehtunguh 2013年

@ shawn-chin解决方案在这里更具Python风格,但是如果您需要保留序列类型,则说您有一个元组而不是列表列表,那么您应该使用reduce(operator.concat,tuple_of_tuples)。与元组一起使用operator.concat似乎比带有list的chain.from_iterables更快。
迈瑟姆(Meitham)2014年

Answers:


4787

给定一个列表列表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

说明:基于快捷方式+(包括中的隐含使用sum)的必要性是O(L**2)当有L个子列表时-随着中间结果列表的长度越来越长,每一步都会分配一个新的中间结果列表对象,并且所有项目必须复制前一个中间结果中的结果(以及最后添加的一些新结果)。因此,为简单起见,而又不失去一般性,请说您有I个项目的L个子列表:第一个I项目来回复制L-1次,第二个I项目L-2次,依此类推;等等。总份数是I乘以x从1到L的x的总和,即I * (L**2)/2

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


486
我尝试了测试使用相同的数据,使用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))'。它的运行速度是嵌套列表理解的两倍以上,这是此处显示的最快替代方法。
直觉

274
我发现语法难以理解,直到我意识到您可以像嵌套for循环一样完全地思考它。对于l中的子列表:对于子列表中的项目:收益项目
Rob Crowell

23
@BorisChervenkov:请注意,我包装了调用list()以实现迭代器到列表中。
直觉

163
[森林中的树上的叶子,树上的叶子中的叶子]可能更容易理解和应用。
John Mee 2013年

80
@Joel,实际上今天list(itertools.chain.from_iterable(l))是最好的-正如其他评论和Shawn的回答所指出的那样。
Alex Martelli'1

1565

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

13
*是一件棘手的事情,chain没有列表理解那么直接。您必须知道,链仅将作为参数传递的可迭代对象连接在一起,而*会使顶级列表扩展为参数,因此chain将所有这些可迭代对象连接在一起,但不会进一步下降。我认为这比在这种情况下使用链更容易理解。
Tim Dierks 2014年

52
@TimDierks:我不确定“这需要您了解Python语法”是否反对在Python中使用给定的技术。当然,复杂的用法可能会造成混淆,但是“ splat”运算符通常在许多情况下很有用,并且并不是以特别晦涩的方式使用它。拒绝所有对于初学者来说不一定很明显的语言功能,意味着您将一只手绑在背后。可能还需要花一些时间去理解列表。来自其他背景的用户会发现一个for反复出现append的循环。
ShadowRanger 2015年

如果顶层还包含一个值,则此答案以及此处的其他答案将给出错误的结果。例如,list = [["abc","bcd"],["cde","def"],"efg"]将导致["abc", "bcd", "cde", "def", "e", "f", "g"].
gouravkr

905

作者注意:这是低效的。但是很有趣,因为类人猿很棒。它不适用于生产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]

请注意,仅适用于列表列表。对于列表列表列表,您将需要其他解决方案。


99
那是非常整洁和聪明的,但是我不会使用它,因为它使阅读变得混乱。
andrewrk 2010年

87
这是画家的算法Sheleiel joelonsoftware.com/articles/fog0000000319.html-不必要的效率低下和丑陋。
Mike Graham

44
列表上的append操作形成a Monoid,这是+从一般意义上考虑操作的最便捷抽象方法之一(不仅限于数字)。因此,对于(正确的)将列表视为类半身像,这个答案值得我+1。表现令人
担忧

7
@andrewrk好吧,有人认为这是最干净的方法:youtube.com/watch? v=IOiZatlZtGU 不明白为什么如此酷的人只需要等待几十年,直到每个人都这样: ),我们使用发现的但未被发明的编程语言(和抽象),即发现Monoid。
jhegedus

11
由于总和的二次方,这是一种非常低效的方式。
让-弗朗索瓦·法布尔

459

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

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

串联多个小列表和几个长列表时,这是最快的解决方案。(operator.iadd同样快。)

在此处输入图片说明

在此处输入图片说明


复制剧情的代码:

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,
    # setup=lambda n: [list(range(n))] * 10,
    kernels=[
        forfor,
        sum_brackets,
        functools_reduce,
        functools_reduce_iconcat,
        itertools_chain,
        numpy_flat,
        numpy_concatenate,
    ],
    n_range=[2 ** k for k in range(16)],
    xlabel="num lists (of length 10)",
    # xlabel="len lists (10 lists total)"
)

25
对于庞大的嵌套列表,“ list(numpy.array(a).flat)”是上述所有函数中最快的。
萨拉(Sara)

使用正则表达式试过:“ list(map(int,re.findall(r“ [\ w] +”,str(a))))'。numpy_concatenate的速度稍慢
Justas

有没有办法做一个3-d perfplot?数组的数量按数组的平均大小?
狮子座

我喜欢您的解决方案。简短,简单,高效:-)
ShadyMBA

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

19
reduce(operator.add, l)是做reduce版本的正确方法。内置函数比lambda更快。
2011年

3
@agf的操作方法如下:* timeit.timeit('reduce(operator.add, l)', 'import operator; l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]', number=10000) 0.017956018447875977 * timeit.timeit('reduce(lambda x, y: x+y, l)', 'import operator; l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]', number=10000) 0.025218963623046875
lukmdo 2012年

8
这是画家的算法Shlemiel joelonsoftware.com/articles/fog0000000319.html
Mike Graham

2
这只能用于integers。但是,如果列表包含该string怎么办?
弗雷迪(Freddy)2015年

3
@Freddy:该operator.add函数对于整数列表和字符串列表都同样有效。
Greg Hewgill

118

如果您使用Django,请不要重新发明轮子:

>>> from django.contrib.admin.utils import flatten
>>> l = [[1,2,3], [4,5], [6]]
>>> flatten(l)
>>> [1, 2, 3, 4, 5, 6]

... 熊猫

>>> from pandas.core.common import flatten
>>> list(flatten(l))

... Itertools

>>> import itertools
>>> flatten = itertools.chain.from_iterable
>>> list(flatten(l))

... Matplotlib

>>> from matplotlib.cbook import flatten
>>> list(flatten(l))

... Unipath

>>> from unipath.path import flatten
>>> list(flatten(l))

... Setuptools

>>> from setuptools.namespaces import flatten
>>> list(flatten(l))

4
flatten = itertools.chain.from_iterable应该是正确的答案
壁虎

3
好答案!在大熊猫的情况下也适用于l = [[[[1,2,3],[4,5]],5]
Markus Dutschke

1
我喜欢熊猫解决方案。如果您有类似的内容:list_of_menuitems = [1, 2, [3, [4, 5, [6]]]],则会在上显示:[1, 2, 3, 4, 5, 6]。我想念的是扁平化水平。
imjoseangel

115

这是适用于数字字符串嵌套列表和混合容器的通用方法。

#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.abc所述typing模块。

演示版

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第三版,O'Reilly Media Inc.,塞巴斯托波尔,加利福尼亚:2013年。
  • 找到了较早的SO帖子,可能是原始的演示。

5
我只是写了几乎一样的东西,因为我没有看到您的解决方案...这就是我在寻找的“递归展平完整多个列表”的内容...(+1)
Martin Thoma

3
@MartinThoma非常感谢。仅供参考,如果扁平化嵌套的可迭代对象是您的常见做法,那么可以使用一些第三方软件包来很好地解决此问题。这可以省去重新发明轮子的麻烦。我已经提到more_itertools了本文中讨论的其他内容。干杯。
pylang

也许traverse对于这种树的方式也可能是个好名字,但是我通过坚持嵌套列表来使它在此答案中的通用性降低。

您可以检查if hasattr(x, '__iter__')而不是导入/检查Iterable,这也将排除字符串。
瑞安·艾伦

如果嵌套列表之一包含字符串列表,则上述代码似乎不起作用。[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]
sunnyX

51

如果要展平不知道嵌套深度的数据结构,可以使用1iteration_utilities.deepflatten

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

只是添加一些时间(基于NicoSchlömer的答案,其中不包括此答案中提供的功能):

在此处输入图片说明

这是一个对数对数图,可以容纳跨度很大的值。对于定性推理:越低越好。

研究结果表明,如果迭代只包含几个内部iterables然后sum将最快,但长期iterables只itertools.chain.from_iterableiteration_utilities.deepflatten或嵌套的理解与合理的性能itertools.chain.from_iterable是最快的(如已被尼科Schlömer注意到)。

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免责声明:我是该图书馆的作者


sum不再对以开头的任意序列起作用0,进行functools.reduce(operator.add, sequences)替换(我们是否很高兴它们reduce从内置函数中删除?)。知道类型后,使用起来可能会更快type.__add__
Yann Vernier

@YannVernier感谢您提供信息。我以为我在Python 3.6上运行了这些基准测试,并且可以与一起使用sum。您是否知道它停止在哪个Python版本上工作?
MSeifert

我有点误会。0只是默认的起始值,因此如果使用start参数以一个空列表开头则可以使用它……但是它仍然是特殊情况,并且告诉我使用join。它正在实现foldl而不是foldl1。2.7中会弹出相同的问题。
Yann Vernier

39

我收回我的声明。总和不是赢家。尽管列表较小时速度更快。但是,列表较大时,性能会大大降低。

>>> 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版本仍在运行一分钟以上,尚未处理!

对于中型列表:

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

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

23
对于一个真正微小的列表,例如一个带有3个子列表的列表,也许-但由于sum的性能与O(N ** 2)关联,而列表理解的与O(N)关联,因此只增加一点输入列表将颠倒一切- -实际上,随着N的增长,LC将“无限快”于极限值之和。我负责设计求和并在Python运行时中执行其第一个实现,但我仍然希望找到一种有效地将其限制为对数字求和(真正擅长的事物)并阻止其给人们带来的“令人讨厌的麻烦”的方法谁想要“汇总”列表;-)。
Alex Martelli,2009年

38

似乎与operator.add!当您将两个列表加在一起时,正确的术语是concat,而不是添加。operator.concat是您需要使用的。

如果您认为功能正常,那么就这么简单:

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

您会看到reduce尊重序列类型,因此在提供元组时,您会得到一个元组。让我们尝试一个列表:

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

啊哈,您会得到一个清单。

性能如何:

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

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

1
嗯,公平地说,第二个例子也应该列出(或第一个元组?)
Mr_and_Mrs_D

2
使用这么小的输入并不是一个公平的比较。对于1000个长度为1000的序列,我得到0.037秒,list(chain.from_iterable(...))而得到2.5秒reduce(concat, ...)。问题是reduce(concat, ...)运行时间chain是平方的,而线性的。
kaya3

33

为什么使用扩展?

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

这应该工作正常。


7
对于python3from functools import reduce
andorov '17

抱歉,这真的很慢,请参阅其余答案
Mr_and_Mrs_D

到目前为止,这是最简单易懂但适用于Python 2和3的简短解决方案。我意识到,许多Python专家都在数据处理中,因为要处理大量数据,因此非常关心速度,但是当您正在编写一个shell脚本,并且在几个子列表中只有几十个元素,那么这很完美。
阿斯范德·卡齐

27

考虑安装more_itertools软件包。

> pip install more_itertools

它附带了一个实现flattensource,来自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.collapsesource,由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]

确实。这应该是公认的答案
brunetton

如果您负担得起向您的项目中添加程序包-最佳答案
viddik13

22

您的函数不起作用的原因是因为扩展名扩展了数组就位并且不返回它。您仍然可以使用以下方法从lambda返回x:

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

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


7
extend是更好的作为newlist = []extend = newlist.extendfor sublist in l: extend(l)因为它避免了(相当大)开销lambda,对属性的查找xor
2011年

为python 3添加from functools import reduce
Markus Dutschke,

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

def flatten(l, a=None): if a is None: a = [][...]
Poik

16

递归版本

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
不错,不需要导入,而且工作情况也很清楚...将列表弄平,::
Goran

1
简直太辉煌了!
萨钦·夏尔马

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]

这比下划线._。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

14

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

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

接受的答案无效

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

新提出的解决方案,没有工作对我来说:

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

13

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

这是一个工作功能:

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

测试:

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]

13

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

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

10

也可以使用NumPy的flat

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

编辑11/02/2016:仅当子列表具有相同尺寸时才可用。


那将是最佳解决方案吗?
RetroCode

6

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


这也适用于数字,字符串和混合列表
Nitin

2
无法成功地嵌套数据,例如[1, 2, [3], [[4]], [5, [6]]]
EL_DON

5

如果您愿意放弃一点速度以获得更干净的外观,则可以使用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

您可以在docs numpy.concatenatenumpy.ravel中找到更多信息


1
不适用于嵌套不均匀的列表,例如[1, 2, [3], [[4]], [5, [6]]]
EL_DON '19

5

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

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

做完了!您当然可以通过执行list(l)将其转换为列表


1
这是错误的,展平会将nd数组的尺寸减小为一,但不会将内部列表串联为一。
安藤·朱莱

5

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

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

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

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]

您可以underscore.py使用pip 安装

pip install underscore.py

同样,您可以使用pydash。我发现此版本比列表理解或任何其他答案更具可读性。
gliemezis

2
这太慢了。
NicoSchlömer'17

2
为什么有一个名为_的模块?那似乎是个坏名字。见stackoverflow.com/a/5893946/6605826
EL_DON '18年

2
@EL_DON:来自underscore.py自述页“ Underscore.py是出色的javascript库underscore.js的python端口”。我想这就是这个名字的原因。是的,它不是为蟒蛇一个好听的名字
武映

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

对于问题中的示例嵌套列表,python2.7失败:[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
EL_DON

@EL_DON已在python 2.7.5上进行了测试。它工作正常
englealuze

5

注意:以下内容适用于Python 3.3+,因为它使用yield_fromsix也是第三方软件包,尽管它很稳定。或者,您可以使用sys.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 s为)。但是,在行与行之间进行一点阅读时,您并不想这样处理-您希望将其视为单个元素。
  • 最后一个元素[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从进行迭代itertools,但还要确保(2)元素不是 “字符串状”的。


1
如果您仍然对Python 2兼容性感兴趣,请更改yield fromfor循环,例如for x in flatten(i): yield x
pylang

5
flat_list = []
for i in list_of_list:
    flat_list+=i

该代码也可以很好地工作,因为它会一直扩展列表。虽然非常相似,但是只有一个for循环。因此,它比添加2 for循环具有更少的复杂性。


5
from nltk import flatten

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

这种解决方案相对于大多数其他解决方案的优势在于,如果您有类似以下的列表:

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

虽然其他大多数解决方案都会引发错误,但此解决方案可以解决这些问题。


该问题指出一个“列表列表”,但是您的示例列表包括一个非列表项。大多数其他解决方案都坚持最初的问题。您的解决方案可以解决更广泛的问题,但它也需要必须首先安装的非基本Python软件包(nltk)。
simonobo

4

这可能不是最有效的方法,但我认为应该放一个衬里(实际上是两个衬里)。两种版本均可在任意层次的嵌套列表上使用,并利用语言功能(Python3.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]

这以深度优先的方式工作。递归向下进行,直到找到一个非列表元素,然后扩展局部变量flist,然后将其回滚到父变量。每当flist返回时,它就会扩展到flist列表理解中的父级。因此,从根本上返回一个平面列表。

上面的代码创建了几个本地列表并返回它们,用于扩展父级列表。我认为解决此问题的方法可能是创建gloabl 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]

尽管目前我不确定效率。


为什么用extend([l])代替append(l)?
Maciek

3

适用于整数的异质和均质列表的另一种异常方法:

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

这只是之前已经贴出的3000英镑的一种更复杂,更慢的方式。我昨天重新提出了他的建议,因此这种方法
当今

不完全是: 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]
tharndt

我作为一名班轮的代码是: flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')]
tharndt

1
您确实是+ 1,,3000的建议不适用于多位数,尽管这很明显,但我也没有进行过测试。您可以简化代码并编写[int(e.strip('[ ]')) for e in str(deep_list).split(',')]。但是我建议坚持使用Deleet的实际用例建议。它不包含hacky类型转换,它更快,更通用,因为它自然也可以处理混合类型的列表。
Darkonaut

2
抱歉不行。但是我最近在这里看到了这段代码:Python练习手册 6.1.2
tharndt
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.