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

Emm*_*mma 2950 python list flatten multidimensional-array

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

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

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

错误信息

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

Ale*_*lli 4260

给出列表清单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)
Run Code Online (Sandbox Code Playgroud)

比目前发布的快捷方式快.(l是要压扁的列表.)

这是相应的功能:

flatten = lambda l: [item for sublist in l for item in sublist]
Run Code Online (Sandbox Code Playgroud)

作为证据,您可以使用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
Run Code Online (Sandbox Code Playgroud)

说明:当存在L个子列表时,基于+(包括隐含用途sum)的快捷方式是必要的O(L**2)- 当中间结果列表持续变长时,在每个步骤分配新的中间结果列表对象,以及所有项目必须复制之前的中间结果(以及最后添加的一些新结果).因此,为了简单而没有实际失去一般性,请说每个项目都有L个子列表:第一个I项目来回复制L-1次,第二个I项目L-2次,依此类推; 总复制数是I乘以x的总和,从1到L排除,即I * (L**2)/2.

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

  • 我尝试使用相同的数据进行测试,使用`itertools.chain.from_iterable`:`$ python -mtimeit -s'来自itertools import chain; l = [[1,2,3],[4,5,6],[7],[8,9]]*99''列表(chain.from_iterable(l))'`.它的运行速度比嵌套列表理解的速度快两倍,这是此处显示的最快替代方案. (443认同)
  • 我发现语法难以理解,直到我意识到你可以把它想象为嵌套for循环.对于子列表中的子列表:对于子列表中的项:yield item (254认同)
  • [树叶中的树叶在树上的叶子]可能更容易理解和应用. (150认同)
  • @Joel,实际上现在`list(itertools.chain.from_iterable(l))`是最好的 - 正如其他评论和Shawn的回答中所注意到的那样. (71认同)
  • @RobCrowell 同样在这里。对我来说,列表理解读起来不正确,感觉有些不对劲——我似乎总是弄错并最终用谷歌搜索。对我来说,这读起来是正确的“[树中的叶子对森林中的树]”。我希望事情就是这样。我确信我在这里遗漏了一些关于语法的内容,如果有人能指出这一点,我将不胜感激。 (53认同)
  • @BorisChervenkov:请注意,我将调用包装在`list()`中以实现迭代器到列表中. (22认同)
  • 不普遍的工作!`l = [1,2,[3,4]] [子列表中项目子项列表中的项目] TypeError:'int'对象不可迭代 (9认同)
  • 只要很难理解,我不关心它的运行速度!任何人都可以告诉我他/她可以记住3个月后的语法而不再参考同一篇文章吗?任何人都可以告诉我为什么你需要重复`item for sublist`?丑丑丑! (5认同)
  • @JohnMee:`[森林中树上树叶的叶子]`本来会更好更容易阅读但遗憾的是,Python开发人员并不这么认为. (5认同)
  • @Sнаđошса׽ 如果您将列表理解视为一系列扁平化的 for 循环,那么这是有意义的。忽略第一个“leaf”,因为这只是您想要填充列表的项目,然后查看一系列“for”循环,它们将嵌套为:第 1 行:“for tree in Forest:”,第 2 行:`对于树中的叶子:`。如果您只是将第二个 for 循环附加到第一个 for 循环,您将获得列表理解的语法。 (5认同)
  • @intuited你的解决方案实际上返回一个**迭代器**,而不是一个实际的列表 - 这就是它运行速度快两倍的原因.参见`type(itertools.chain([[1,2],[3,4]]))`.但是如果@emma需要嵌套列表来迭代它 - 这是一个很好的最佳解决方案:o) (4认同)
  • 为什么所有的赞成?`reduce(operator.concat,list2d)`更快更容易理解!见[这个答案](http://stackoverflow.com/a/39493960/2529619). (4认同)
  • 每次我想展平一个列表时,我都一直在看这里,但这个 gif 是把它带回家的原因:https://i.stack.imgur.com/0GoV5.gif (4认同)
  • 如果您的列表元素是字符串,则解决方案将失败,因为它们也被展平为单个字符...... (4认同)
  • @AlexMartelli你说"现在`列表(itertools.chain.from_iterable(l))``是最好的".您可以将其添加到答案的顶部,并提供更好答案的链接吗?你的答案有很多赞成,似乎有权威! (3认同)
  • @Sнаđошса׽我有同样的感觉,但这样想:变量必须先定义,然后才能访问。在“[leaf for leaf in tree for tree in Forest]”中,首次使用“in”访问时,“tree”还不存在。它仅由“森林中的树”定义。因此,需要写“[森林中的叶子换树中的叶子]”。是的,我知道,但这并不适用于第一片“叶子”……(而且我并不是说这就是事情内部的实际工作方式。) (3认同)
  • @Noio如果你重新订购它是有道理的:`[l子列表中的项目项目在l]`.当然,如果你重新订购它,那么对*Python*没有意义,因为你在定义它之前使用`sublist`. (2认同)
  • @Sven它适用于任何列表列表; `[1,2,[3,4]]`不是列表清单.你可以用这样的方式将这个特定案例的单行解决方案混合在一起:`[子列表中的子列表如果是实例(子列表,列表),则子列表中的子列表,子列表中的子列表)子列表中的项目``; 但这并不简洁,值得装入一条线.或者,它很简单,可以自己编写一个用于展平任意嵌套序列的函数; 另见http://stackoverflow.com/a/2158532/2359271 (2认同)
  • @JuanXarg,不,你完全误解了'timeit`的输出:它再次迭代10次因为它*可以*(在大约相同的时间内占用相同的时间),而不是因为迭代是在任何方式,形状或形式,"必需". (2认同)

Sha*_*hin 1388

你可以使用itertools.chain():

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

或者,在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))
Run Code Online (Sandbox Code Playgroud)

这种方法可以说更具可读性,*而且看起来也更快:

$ 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
Run Code Online (Sandbox Code Playgroud)

  • @TimDierks:我不确定"这需要你理解Python语法"是反对在Python中使用给定技术的论据.当然,复杂的使用可能会混淆,但"splat"操作符在许多情况下通常都很有用,而且这种操作并不是以特别模糊的方式使用它.拒绝所有对初学者来说不一定显而易见的语言特征意味着你将一只手绑在背后.也可以在你做的时候抛弃列表理解; 来自其他背景的用户会发现一个`for`循环,反复"追加"更明显. (36认同)
  • `*`是一个棘手的事情,使得'链'比列表理解更简单.你必须知道链只将作为参数传递的迭代连接在一起,并且*导致顶级列表扩展为参数,因此`chain`将所有这些迭代连接在一起,但不会进一步下降.我认为这使得理解比在这种情况下使用链更具可读性. (8认同)
  • * 创建一个中间元组。!`from_iterable` 直接从顶部列表中获取嵌套列表。 (4认同)
  • 更短的是 `[*chain(*l)]` _([`python3.5+`](https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-概括),2015 年发布)_ (3认同)
  • 为了使其更具可读性,您可以创建一个简单的函数:`def flatten_list(deep_list: list[list[object]]):``return list(chain.from_iterable(deep_list))`。类型提示提高了所发生情况的清晰度(现代 IDE 会将其解释为返回“list[object]”类型)。 (2认同)

Tri*_*ych 819

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

>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 这是非常整洁和聪明但我不会使用它因为它令人困惑阅读. (92认同)
  • 这是Shlemiel画家的算法http://www.joelonsoftware.com/articles/fog0000000319.html - 不必要的低效率以及不必要的丑陋. (81认同)
  • 列表上的追加操作形成了一个**[`Monoid`](http://en.wikipedia.org/wiki/Monoid#Monoids_in_computer_science)**,这是思考`+`操作最方便的抽象之一在一般意义上(不仅限于数字).所以这个答案值得给我+1一个(正确)处理列表作为幺半群.*表现虽然......* (36认同)
  • 由于总和的二次方面,这是一种非常低效的方式. (8认同)
  • @andrewrk嗯,有些人认为这是最干净的方式:https://www.youtube.com/watch?v =IOiZatlZtGU那些不明白为什么这很酷的人只需要等待几十年才能每个人都这样做:)让我们使用被发现但未被发明的编程语言(和抽象),Monoid被发现. (6认同)
  • 本文介绍了效率低下的数学方法https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/ (3认同)

Nic*_*mer 348

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

functools.reduce(operator.iconcat, a, [])
Run Code Online (Sandbox Code Playgroud)

成为最快的解决方案.(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,
    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'
    )
Run Code Online (Sandbox Code Playgroud)

  • 对于巨大的嵌套列表,'list(numpy.array(a).flat)'是上述所有函数中最快的. (19认同)
  • 在 Rossetta 代码([链接](https://rosettacode.org/wiki/Flatten_a_list#Python))的测试示例上尝试了“numpy_flat”,并得到了“VisibleDeprecationWarning:从参差不齐的嵌套序列创建一个 ndarray(这是一个列表或-具有不同长度或形状的列表或元组或ndarray的元组)已被弃用。如果您打算这样做,则必须在创建 ndarray 时指定 'dtype=object' (6认同)
  • 对于不同的子列表长度,np.array `flat` **不起作用**。例如, `a = [ [1,2], [1,2,3]]` `list(np.array(a).flat)` 将返回原始列表。使用 list(np.concatenate(a)) 更安全 (4认同)
  • @Sara你能定义“巨大”吗? (3认同)
  • 有没有办法制作 3-d perfplot?数组的数量除以数组的平均大小? (2认同)
  • 上面遗漏了一个选项,对于我的特定情况,它显示得更快,我只是 `items = []; 对于 a 中的子列表: items.extend(sublist); 返回子列表` (2认同)
  • @Sara,如果元素列表的长度不同,解决方案就会失败。例如:“[[1, 2], [3, 4], [5]]” (2认同)

Gre*_*ill 158

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]
Run Code Online (Sandbox Code Playgroud)

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]
Run Code Online (Sandbox Code Playgroud)

  • `reduce(operator.add,l)`是执行`reduce`版本的正确方法.内置命令比lambdas快. (14认同)
  • 这是Shlemiel画家的算法joelonsoftware.com/articles/fog0000000319.html (8认同)
  • @Freddy:`operator.add`函数同样适用于整数列表和字符串列表. (3认同)
  • @agf的操作如下:*`timeit.timeit('reduce(operator.add,l)','导入运算符; l = [[1、2、3],[4、5、6、7、8], [1、2、3、4、5、6、7]]',数字= 10000)`** 0.017956018447875977 ** *`timeit.timeit('reduce(lambda x,y:x + y,l)',, '导入运算符; l = [[1、2、3],[4、5、6、7、8],[1、2、3、4、5、6、7]],数字= 10000)” ** 0.025218963623046875 ** (2认同)
  • 这只能用于"整数".但是如果list包含`string`怎么办? (2认同)

pyl*_*ang 96

以下是适用于数字,字符串,嵌套列表和混合容器的一般方法.

#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
Run Code Online (Sandbox Code Playgroud)

注意:在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']
Run Code Online (Sandbox Code Playgroud)

参考

  • 该解决方案是根据Beazley,D.和B. Jones的配方修改的.食谱4.14,Python Cookbook 3rd Ed.,O'Reilly Media Inc. Sebastopol,CA:2013.
  • 找到了早期的SO帖子,可能是最初的演示.

  • 我写的几乎是一样的,因为我没有看到你的解决方案......这就是我所寻找的"递归展平完整的多个列表"...(+ 1) (4认同)
  • @MartinThoma非常感谢.仅供参考,如果扁平化嵌套迭代是一种常见的做法,那么有一些第三方软件包可以很好地处理这个问题.这可以避免重新发明轮子.我在这篇文章中讨论过其他人提到的`more_itertools`.干杯. (3认同)

MSe*_*ert 43

如果你想展平一个你不知道嵌套深度的数据结构,你可以使用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]
Run Code Online (Sandbox Code Playgroud)

它是一个生成器,因此您需要将结果转换为a 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]
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

这是一个对数日志图,可以适应各种各样的值.对于定性推理:越低越好.

研究结果表明,如果迭代只包含几个内部iterables然后sum将最快,但长期iterables只itertools.chain.from_iterable,iteration_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()
Run Code Online (Sandbox Code Playgroud)

1免责声明:我是该图书馆的作者


Max*_*ysh 39

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

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

... 熊猫

>>> from pandas.core.common import flatten
>>> list(flatten(l))
Run Code Online (Sandbox Code Playgroud)

... Itertools

>>> import itertools
>>> flatten = itertools.chain.from_iterable
>>> list(flatten(l))
Run Code Online (Sandbox Code Playgroud)

... Matplotlib

>>> from matplotlib.cbook import flatten
>>> list(flatten(l))
Run Code Online (Sandbox Code Playgroud)

... Unipath

>>> from unipath.path import flatten
>>> list(flatten(l))
Run Code Online (Sandbox Code Playgroud)

... Setuptools

>>> from setuptools.namespaces import flatten
>>> list(flatten(l))
Run Code Online (Sandbox Code Playgroud)

  • `flatten = itertools.chain.from_iterable`应该是正确的答案 (2认同)

Nad*_*mli 37

我接受我的陈述.总和不是赢家.虽然列表很小但速度更快.但是更大的列表会使性能显着下降.

>>> 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
Run Code Online (Sandbox Code Playgroud)

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

对于中等列表:

>>> 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
Run Code Online (Sandbox Code Playgroud)

使用小列表和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
Run Code Online (Sandbox Code Playgroud)

  • 对于一个真正微不足道的列表,例如一个有3个子列表,可能 - 但由于sum的性能与O(N**2)一致,而列表理解与O(N)一致,只是增加输入列表有点会扭转事物 - - 实际上,当N增长时,LC将比限制时的总和"无限快".我负责设计sum并在Python运行时进行第一次实现,我仍然希望我找到一种方法来有效地限制它来汇总数字(它真正擅长)并阻止它为人们提供的"有吸引力的滋扰"谁想要"总结"清单;-). (23认同)

dtl*_*m26 36

根据你的列表[[1, 2, 3], [4, 5, 6], [7], [8, 9]]是1级列表,我们可以简单地使用sum(list,[])而不使用任何库

sum([[1, 2, 3], [4, 5, 6], [7], [8, 9]],[])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
Run Code Online (Sandbox Code Playgroud)

当内部存在元组或数字时,扩展此方法的优点。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]
Run Code Online (Sandbox Code Playgroud)

这里,清楚地解释了这种方法在内存方面的缺点。简而言之,它递归地创建列表对象,这是应该避免的:(

  • 这个答案已经在这个问题中:/sf/answers/66706251/ (5认同)
  • 整洁的!尽管这里的另一个答案,/sf/answers/66706251/,解释了这个解决方案**通常应该避免**的原因(它效率低下且令人困惑。) (3认同)
  • 如果您的列表包含元组,还会给出 TypeError (2认同)

Mei*_*ham 34

似乎有一种混乱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)
Run Code Online (Sandbox Code Playgroud)

你看到reduce尊重序列类型,所以当你提供一个元组时,你会得到一个元组.让我们尝试一下清单::

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

啊哈,你得到一份清单.

性能怎么样::

>>> 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)


And*_*mbu 31

为什么使用extend?

reduce(lambda x, y: x+y, l)
Run Code Online (Sandbox Code Playgroud)

这应该工作正常.

  • 这可能会创建许多很多中间列表. (14认同)
  • for python3```来自functools import reduce``` (6认同)

pyl*_*ang 22

考虑安装more_itertools包.

> pip install more_itertools
Run Code Online (Sandbox Code Playgroud)

它附带了一个实现flatten(源代码,来自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]
Run Code Online (Sandbox Code Playgroud)

从版本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]
Run Code Online (Sandbox Code Playgroud)


Igo*_*kon 20

您的函数不起作用的原因:extend将数组扩展到原位并且不返回它.您仍然可以使用一些技巧从lambda返回x:

reduce(lambda x,y: x.extend(y) or x, l)
Run Code Online (Sandbox Code Playgroud)

注意:extend比列表上的+更有效.

  • `extend`最好用作`newlist = []`,`extend = newlist.extend`,`用于l:extend(l)`中的子列表,因为它避免了`lambda`的(相当大的)开销,属性查询`x`和`或`. (6认同)

Ani*_*nil 14

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]
Run Code Online (Sandbox Code Playgroud)


Del*_*eet 12

上面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
Run Code Online (Sandbox Code Playgroud)

测试:

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]
Run Code Online (Sandbox Code Playgroud)


use*_*332 12

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

l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]
Run Code Online (Sandbox Code Playgroud)

接受的答案是根本没有工作:

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']
Run Code Online (Sandbox Code Playgroud)

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

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']
Run Code Online (Sandbox Code Playgroud)


小智 12

递归版本

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]
Run Code Online (Sandbox Code Playgroud)


mmj*_*mmj 12

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

def flatten(itr):
    for x in itr:
        try:
            yield from flatten(x)
        except TypeError:
            yield x
Run Code Online (Sandbox Code Playgroud)

用法:这是一个生成器,您通常希望将其包含在可迭代的构建器中,例如list()ortuple()或在循环中使用它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
Run Code Online (Sandbox Code Playgroud)


EL_*_*DON 11

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))
Run Code Online (Sandbox Code Playgroud)

结果:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Run Code Online (Sandbox Code Playgroud)

这比下划线快18倍._.flatten:

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
Run Code Online (Sandbox Code Playgroud)


dev*_*ail 9

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

>>> 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]
Run Code Online (Sandbox Code Playgroud)

  • OP 没有提到他们想使用 numpy。Python 有很好的方法可以在不依赖库的情况下做到这一点 (4认同)
  • 不适用于已经很平坦的数组,请考虑:`import numpy as np l = [1,2,3] print(np.concatenate(l))`ValueError:零维数组无法连接。 (2认同)

mdh*_*mdh 8

也可以使用NumPy的公寓:

import numpy as np
list(np.array(l).flat)
Run Code Online (Sandbox Code Playgroud)

编辑11/02/2016:仅当子列表具有相同的尺寸时才有效.


rus*_*ro1 8

您可以使用list extend方法,它显示是最快的:

flat_list = []
for sublist in l:
    flat_list.extend(sublist)
Run Code Online (Sandbox Code Playgroud)

表现:

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',
    )
Run Code Online (Sandbox Code Playgroud)

输出: 在此处输入图片说明


mku*_*tra 6

如果您愿意放弃少量速度以获得更清洁的外观,那么您可以使用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
Run Code Online (Sandbox Code Playgroud)

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

  • 不适用于不均匀嵌套的列表,如“[1, 2, [3], [[4]], [5, [6]]]” (2认同)

A. *_*tia 6

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


Vu *_*Anh 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]
Run Code Online (Sandbox Code Playgroud)

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

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]
Run Code Online (Sandbox Code Playgroud)

您可以underscore.py使用pip 安装

pip install underscore.py
Run Code Online (Sandbox Code Playgroud)

  • 这太慢了。 (2认同)
  • 为什么它有一个名为_的模块?那似乎是个坏名字。参见/sf/answers/412576251/ (2认同)
  • @EL_DON:来自underscore.py自述页面“ Underscore.py是出色的javascript库underscore.js的python端口”。我想这就是这个名字的原因。是的,这不是python的好名字 (2认同)

eng*_*uze 5

def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
Run Code Online (Sandbox Code Playgroud)


Bra*_*mon 5

注意:以下适用于 Python 3.3+,因为它使用yield_from. six也是第三方包,虽然它很稳定。或者,您可以使用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]]]
Run Code Online (Sandbox Code Playgroud)

这里有几个问题:

  • 一个元素,6,只是一个标量;它不是可迭代的,所以上面的路线在这里会失败。
  • 其中一个要素,'abc'技术上可迭代(所有strs为)。但是,在两行之间稍微阅读一下,您不想将其视为这样 - 您想将其视为单个元素。
  • 最后一个元素[8, [9, 10]]本身是一个嵌套的可迭代对象。基本列表理解,chain.from_iterable只提取“1级”。

您可以通过以下方式解决此问题:

>>> 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]
Run Code Online (Sandbox Code Playgroud)

在这里,您检查子元素 (1) 是否可使用Iterable、来自 的 ABC 进行迭代itertools,但还希望确保 (2) 元素不是“类似字符串的”。


Dee*_*dav 5

flat_list = []
for i in list_of_list:
    flat_list+=i
Run Code Online (Sandbox Code Playgroud)

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