4528

是否有从 Python 中的列表列表中创建一个简单列表的快捷方式?

我可以for循环执行,但是有一些很酷的“单线”吗?

我试过了functools.reduce()

from functools import 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'
4

26 回答 26

6403

给定一个列表列表t

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

意思是:

flat_list = []
for sublist in t:
    for item in sublist:
        flat_list.append(item)

比迄今为止发布的快捷方式更快。(t是要展平的列表。)

下面是对应的函数:

def flatten(t):
    return [item for sublist in t for item in sublist]

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

$ python -mtimeit -s't=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in t for item in sublist]'
10000 loops, best of 3: 143 usec per loop
$ python -mtimeit -s't=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(t, [])'
1000 loops, best of 3: 969 usec per loop
$ python -mtimeit -s't=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,t)'
1000 loops, best of 3: 1.1 msec per loop

说明:当有 T 个子列表时,基于+(包括隐含使用sum)的快捷方式必然是——随着中间结果列表越来越长,每一步都会分配一个新的中间结果列表对象,并且所有项目O(T**2)必须复制之前的中间结果(以及最后添加的一些新结果)。因此,为简单起见且不失一般性,假设您有 T 个子列表,每个子列表有 k 个项目:前 k 个项目被来回复制 T-1 次,后 k 个项目被复制 T-2 次,依此类推;总副本数是 x 的总和的 k 倍,对于从 1 到 T 排除的 x,即k * (T**2)/2.

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

于 2009-06-04T20:37:01.207 回答
2005

您可以使用itertools.chain()

import itertools

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

或者您可以使用itertools.chain.from_iterable()which 不需要使用*操作员解包列表:

merged = list(itertools.chain.from_iterable(list2d))
于 2009-06-04T21:06:17.763 回答
1170

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

>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> 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]

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

于 2009-06-04T20:35:53.563 回答
760

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

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

成为最快的解决方案,无论是在连接许多小列表和少数长列表时。(operator.iadd同样快。)

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

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

如果子列表的数量很大,这会比上面的建议差一点。

在此处输入图像描述

在此处输入图像描述


重现情节的代码:

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()
于 2017-07-26T09:38:16.403 回答
262
>>> from functools import reduce
>>> 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而不是返回有用的值(期望值functools.reduce())。

一个更快的方法来做这个reduce版本是

>>> from functools import 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]
于 2009-06-04T20:35:30.430 回答
157

这是一种适用于数字字符串嵌套列表和混合容器的通用方法。这可以扁平化简单和复杂的容器(另请参见Demo)。

代码

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的配方修改的。配方 4.14,Python Cookbook 第 3 版,O'Reilly Media Inc. Sebastopol,CA:2013。
  • 找到了一个较早的SO 帖子,可能是原始演示。
于 2016-11-29T04:14:45.637 回答
87

如果你想扁平化一个你不知道嵌套深度的数据结构,你可以使用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]

它是一个生成器,因此您需要将结果转换为 alist或显式迭代它。


要仅展平一个级别,并且如果每个项目本身都是可迭代的,您还可以使用iteration_utilities.flattenwhich 本身只是一个薄包装器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 的答案,不包括此答案中提供的功能):

在此处输入图像描述

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

结果表明,如果可迭代对象仅包含几个内部可迭代对象,那么sum将是最快的,但是对于长可迭代对象itertools.chain.from_iterable,只有iteration_utilities.deepflatten或嵌套推导具有最快的合理性能itertools.chain.from_iterable(正如 Nico 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 免责声明:我是那个图书馆的作者

于 2016-11-26T00:20:37.347 回答
39

考虑安装more_itertools软件包。

> pip install more_itertools

它附带了一个实现flattensource,来自itertools recipes):

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]

注意:如docs中所述,flatten需要一个列表列表。请参阅下面的扁平化更多不规则输入。


more_itertools.collapse从 2.4 版开始,您可以使用( source,由 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]
于 2016-12-02T18:35:17.803 回答
35

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

>>> 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]
于 2017-07-05T05:14:38.003 回答
32

您的函数不起作用的原因是因为extend就地扩展了一个数组并且不返回它。您仍然可以使用以下内容从 lambda 返回 x:

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

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

于 2009-06-04T20:47:13.743 回答
23

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
于 2018-02-01T18:22:49.197 回答
16

也可以使用 NumPy 的flat

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

它仅在子列表具有相同尺寸时才有效。

于 2016-07-17T12:57:48.257 回答
16

根据您[[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]
于 2021-12-09T09:15:47.340 回答
10

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

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

输出: 在此处输入图像描述

于 2020-01-25T21:08:38.327 回答
8

如果您愿意放弃一点速度以获得更干净的外观,那么您可以使用numpy.concatenate().tolist()or 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.concatenatenumpy.ravel中找到更多信息。

于 2016-10-27T03:24:08.400 回答
8

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

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

用法:这是一个生成器,您通常希望将它包含在一个可迭代的构建器中,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
于 2020-08-08T14:52:46.973 回答
6

注意:以下适用于 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]]]

这里有几个问题:

  • 一个元素 ,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]

在这里,您检查子元素 (1) 是否可与IterableABC一起迭代itertools,但还希望确保 (2) 元素不是“类似字符串的”。

于 2018-02-01T18:33:22.320 回答
5
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
于 2017-08-08T14:59:07.703 回答
5

for在列表理解中使用两个:

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
flat_l = [e for v in l for e in v]
print(flat_l)
于 2022-01-08T07:55:39.647 回答
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]

虽然我目前不确定效率。

于 2018-05-16T09:41:57.887 回答
2

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

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(',')]
于 2018-01-09T14:34:15.477 回答
2

我想要一个可以处理多个嵌套的解决方案([[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
于 2021-10-07T17:38:30.240 回答
1

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

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]
于 2021-12-09T06:10:17.333 回答
0

您可以使用以下内容:

def flatlst(lista):
    listaplana = []
    for k in lista: listaplana = listaplana + k
    return listaplana
于 2021-10-07T16:02:04.623 回答
0

如果我想在以前的好答案中添加一些东西,这是我的递归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]
于 2022-01-11T15:56:21.417 回答
-3
np.hstack(listoflist).tolist()
于 2020-11-06T17:04:00.210 回答