ChatGPT解决这个技术问题 Extra ChatGPT

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

我想展平这个列表列表:

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

进入:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
此处对此进行了深入讨论:rightfootin.blogspot.com/2006/09/more-on-python-flatten.html,讨论了几种扁平化任意嵌套列表的方法。一个有趣的阅读!
stackoverflow.com/questions/50259290/…(本文解释了 np.flatten() 和 tf.flatten() 使用(静态与动态)ndarray 之间的区别。
这是一个非常常见的重复目标。但是,对于 OP 已经有一个生成列表列表的过程(特别是如果它是列表推导式)的情况,请考虑 stackoverflow.com/questions/1077015/… 是否是更适用的副本。

M
Mateen Ulhaq

要展平列表 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't=[[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'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

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


我尝试使用相同的数据进行测试,使用 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))'。它的运行速度是此处显示的替代方法中最快的嵌套列表推导式的两倍多。
我发现语法很难理解,直到我意识到你可以把它想象成嵌套的 for 循环。对于 l 中的子列表:对于子列表中的项目:产量项目
[以林换树,以树换叶]可能更容易理解和应用。
@RobCrowell 这里也一样。对我来说,列表理解并不阅读正确,感觉有些不对劲——我似乎总是弄错并最终在谷歌上搜索。对我来说,这读起来是正确的 [leaf for leaf in tree for tree in forest]。我希望事情是这样的。我确信我在这里遗漏了一些关于语法的东西,如果有人能指出这一点,我将不胜感激。
每次我想要展平列表时,我都会一直在这里寻找,但这个 gif 是它的驱动力:i.stack.imgur.com/0GoV5.gif
S
Shawn Chin

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

* 是使 chain 不如列表理解那么简单的棘手问题。您必须知道,chain 仅将作为参数传递的迭代连接在一起,而 * 导致顶级列表扩展为参数,因此 chain 将所有这些迭代连接在一起,但不会进一步下降。我认为这使得理解比在这种情况下使用链更具可读性。
@TimDierks:我不确定“这需要您理解 Python 语法”是反对在 Python 中使用给定技术的论据。当然,复杂的用法可能会让人感到困惑,但是“splat”运算符通常在许多情况下都很有用,而且这并不是以一种特别晦涩的方式使用它;拒绝所有对初学者来说不一定显而易见的语言功能意味着您将一只手绑在背后。当你在它的时候,也可以扔掉列表推导;来自其他背景的用户会发现for 循环反复append 更加明显。
创建一个中间元组。! from_iterable 直接从顶部列表中获取嵌套列表。
为了使其更具可读性,您可以创建一个简单的函数:def flatten_list(deep_list: list[list[object]]): return list(chain.from_iterable(deep_list))。类型提示提高了正在发生的事情的清晰度(现代 IDE 会将其解释为返回 list[object] 类型)。
M
Mateen Ulhaq

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

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

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


这非常整洁和聪明,但我不会使用它,因为它阅读起来很混乱。
这是画家的 Shlemiel 算法joelonsoftware.com/articles/fog0000000319.html——不必要的低效和不必要的丑陋。
列表上的追加操作形成 Monoid,这是在一般意义上考虑 + 操作的最方便的抽象之一(不仅限于数字)。因此,对于将列表(正确)处理为幺半群,这个答案值得我 +1。 虽然性能令人担忧......
由于总和的二次方,这是一种非常低效的方法。
N
Nico Schlömer

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

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

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

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

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

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

https://i.stack.imgur.com/82YEG.png

https://i.stack.imgur.com/Mo7iy.png

重现情节的代码:

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

对于巨大的嵌套列表,'list(numpy.array(a).flat)' 是上述所有函数中最快的。
有没有办法做一个 3-d perfplot?数组的平均大小是多少?
@Sara 你能定义“巨大”吗?
在 Rossetta Code (link) 的测试示例中尝试了 numpy_flat,得到了 VisibleDeprecationWarning: 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
上面遗漏了一个选项,对于我的特定情况,它显示得更快,我只是 items = []; for sublist in a: items.extend(sublist); return sublist
M
Mateen Ulhaq

使用 functools.reduce,它将累积列表 xs 添加到下一个列表 ys

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]

reduce(operator.concat, l) 就像一个魅力。添加 sorted(list(set(reduce(operator.concat, l))) 以从列表列表中获取 sorted listunique 个值。
p
pylang

这是适用于数字、字符串、嵌套列表和混合容器的通用方法。这可以使简单和复杂的容器变平(另见演示)。

代码

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 中,flatten(x) 中的 yield 可以替换 flatten(x) 中的 sub_x:yield sub_x

在 Python 3.8 中,抽象基类从 collection.abc 移到了类型模块。

演示

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 帖子,可能是原始演示。


我只是写了几乎相同的内容,因为我没有看到您的解决方案……这是我寻找的“递归展平完整的多个列表”……(+1)
@MartinThoma 非常感谢。仅供参考,如果扁平化嵌套迭代对您来说是一种常见的做法,那么有一些第三方包可以很好地处理这个问题。这可以避免重新发明轮子。在这篇文章中讨论的其他内容中,我提到了 more_itertools。干杯。
也许 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]
M
Mateen Ulhaq

要展平深度嵌套的数据结构,请使用 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's answer):

https://i.stack.imgur.com/3D8uN.png

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

结果表明,如果可迭代对象仅包含少数内部可迭代对象,则 sum 将是最快的,但对于长可迭代对象,只有 itertools.chain.from_iterableiteration_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()

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


F
Francisco

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

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

OP 没有提到他们想使用 numpy。 Python 有很好的方法来做到这一点,而不依赖于库
p
pylang

考虑安装 more_itertools 软件包。

> pip install more_itertools

它附带了 flatten 的实现(source,来自 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 需要一个列表列表。请参阅下面的扁平化更多不规则输入。

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

如果您有能力为您的项目添加一个包 - 这个答案是最好的
当所有元素都未列出时,它会失败。 (例如 lst=[1, [2,3]])。当然整数是不可迭代的。
另外,请注意字符串列表将被展平为字符列表
S
Shan-mk

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

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

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


extend 最好用作 newlist = []extend = newlist.extendfor sublist in l: extend(l),因为它避免了 lambdax 上的属性查找和 or 的(相当大的)开销。
对于 python 3 添加 from functools import reduce
F
Francisco

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

d
dtlam26

根据您的列表 [[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]

当内部存在元组或数字时,扩展此方法的优势。只需将 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]

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


这个答案已经出现在这个问题中:stackoverflow.com/a/952946/14273548
整洁的!尽管此处的另一个答案 stackoverflow.com/a/952946/14273548 解释了该解决方案通常应避免的原因(它效率低下且令人困惑。)
如果您的列表包含元组,也会给出 TypeError
P
Peter Mortensen

也可以使用 NumPy 的 flat

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

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


T
Tuhin Paul

在列表推导中使用两个 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)

我只是在python代码库中看到了这种类型的表达式,它不是最直观的。
这与 accepted answer 完全相同(但没有任何解释/其他有用的补充),所以我投了反对票。
我更喜欢简洁的答案。答案提到了“列表理解”,可以在 python 文档中轻松查找。
P
Peter Mortensen

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

输出:

https://i.stack.imgur.com/xMrjA.png


P
Peter Mortensen

有几个答案与以下相同的递归附加方案,但没有一个使用 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

为什么要使用元组?现在您的解决方案效率低下。
对于任何序列,sum((flatten(e) for e in itr), tuple()) 的效率都非常低,
@juanpa.arrivillaga 你的评论让我考虑改进我的答案,我想我找到了一个更好的答案,你觉得呢?
P
Peter Mortensen

如果您愿意为了更干净的外观而放弃一点点速度,那么您可以使用 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.concatenatenumpy.ravel 中找到更多信息。


不适用于 [1, 2, [3], [[4]], [5, [6]]] 等不均匀嵌套的列表
@juanpa.arrivillaga 不过,这是对这个问题的简单而自然的延伸。可以处理更大嵌套深度的答案更有可能对发现此问题的人有用。
B
Brad Solomon

注意:以下适用于 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 都是)。但是,在字里行间稍微阅读一下,您不想将其视为这样-您想将其视为单个元素。

最后一个元素 [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(来自 itertools 的 ABC)进行迭代,但还希望确保 (2) 元素是 not "类似字符串的。”


如果您仍然对 Python 2 兼容性感兴趣,请将 yield from 更改为 for 循环,例如 for x in flatten(i): yield x
e
englealuze
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]]
P
Peter Mortensen

这可能不是最有效的方法,但我想放一个单线(实际上是两线)。这两个版本都适用于任意层次的嵌套列表,并利用语言特性(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]

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

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

虽然我目前不确定效率。


为什么要扩展([l])而不是附加(l)?
A
Alon Gouldman

我想要一个可以处理多个嵌套的解决方案(例如 [[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

P
Peter Mortensen

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

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

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

t
tharndt

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

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]
我作为一个班轮的代码是:flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')]
你确实是对的 +1,ᴡʜᴀᴄᴋᴀᴍᴀᴅᴏᴏᴅʟᴇ3000 的提议不适用于多位数字,我之前也没有测试过,尽管它应该很明显。您可以简化代码并编写 [int(e.strip('[ ]')) for e in str(deep_list).split(',')]。但我建议坚持 Deleet 对实际用例的建议。它不包含 hacky 类型转换,它更快、更通用,因为它自然也处理混合类型的列表。
抱歉不行。但我最近在这里看到了这段代码:Python Practice Book 6.1.2
佚名

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

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]

S
S.B

您可以使用以下内容:

def flatlst(lista):
    listaplana = []
    for k in lista: listaplana = listaplana + k
    return listaplana

+ 运算符每次都会创建一个新列表。最好使用 +=.extend()
V
Vova

我建议使用带有 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)

P
Peter Mortensen

如果我想在之前的优秀答案中添加一些内容,这里是我的递归 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]

J
Jayesh Chandrapal
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]

B
BhushanD

考虑到列表只有整数:

import re
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(map(int,re.sub('(\[|\])','',str(l)).split(',')))

S
Suraj Rao
np.hstack(listoflist).tolist()

虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。考虑阅读 How to Answeredit 您的答案以改进它。