是的,我知道这个主题之前已经被讨论过了:

Python成语链(扁平化)有限迭代对象的无限迭代? 在Python中扁平化一个浅列表 理解平展一个序列的序列吗? 我如何从列表的列表中创建一个平面列表?

但据我所知,所有的解决方案,除了一个,在像[[[1,2,3],[4,5]],6]这样的列表上失败,其中期望的输出是[1,2,3,4,5,6](或者更好,一个迭代器)。

我看到的唯一解决方案,适用于任意嵌套是在这个问题:

def flatten(x):
    result = []
    for el in x:
        if hasattr(el, "__iter__") and not isinstance(el, basestring):
            result.extend(flatten(el))
        else:
            result.append(el)
    return result

这是最好的方法吗?我是不是忽略了什么?任何问题吗?


当前回答

用Python 3迭代解决

此解决方案可用于除str和bytes以外的所有对象。

from collections import Iterable
from collections import Iterator


def flat_iter(obj):
    stack = [obj]
    while stack:
        element = stack.pop()
        if element and isinstance(element, Iterator):
            stack.append(element)
            try:
                stack.append(next(element))
            except StopIteration:
                stack.pop()
        elif isinstance(element, Iterable) and not isinstance(element, (str, bytes)):
            stack.append(iter(element))
        else:
            yield element


tree_list = [[(1,2,3),(4,5,6, (7,8, 'next element is 5')), (5,6), [[[3,4,5],'foo1'],'foo2'],'foo3']]

not_iterable = 10

it1 = flat_iter(tree_list)
it2 = flat_iter(not_iterable)

print(list(it1))
print(list(it2))

[1, 2, 3, 4, 5, 6, 7, 8,下一个元素是5,5,6,3,4,5,‘foo1’,‘foo2’,‘foo3’) [10]

其他回答

我的解决方案:

import collections


def flatten(x):
    if isinstance(x, collections.Iterable):
        return [a for i in x for a in flatten(i)]
    else:
        return [x]

更简洁一点,但基本相同。

我没有在这里讨论所有已经可用的答案,但这里有一个我想到的语句,借鉴了lisp的第一和其余列表处理方式

def flatten(l): return flatten(l[0]) + (flatten(l[1:]) if len(l) > 1 else []) if type(l) is list else [l]

这里有一个简单的和一个不那么简单的例子

>>> flatten([1,[2,3],4])
[1, 2, 3, 4]

>>> flatten([1, [2, 3], 4, [5, [6, {'name': 'some_name', 'age':30}, 7]], [8, 9, [10, [11, [12, [13, {'some', 'set'}, 14, [15, 'some_string'], 16], 17, 18], 19], 20], 21, 22, [23, 24], 25], 26, 27, 28, 29, 30])
[1, 2, 3, 4, 5, 6, {'age': 30, 'name': 'some_name'}, 7, 8, 9, 10, 11, 12, 13, set(['set', 'some']), 14, 15, 'some_string', 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
>>> 

我不确定这是否更快或更有效,但这就是我所做的:

def flatten(lst):
    return eval('[' + str(lst).replace('[', '').replace(']', '') + ']')

L = [[[1, 2, 3], [4, 5]], 6]
print(flatten(L))

这里的flatten函数将列表转换为一个字符串,去掉所有的方括号,将方括号附加到末尾,并将其转换为列表。

但是,如果你知道你的列表中会有方括号,比如[[1,2],"[3,4]和[5]"],你就必须做一些其他的事情。

你可以使用第三方包iteration_utilities中的deepflatten:

>>> from iteration_utilities import deepflatten
>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(deepflatten(L))
[1, 2, 3, 4, 5, 6]

>>> list(deepflatten(L, types=list))  # only flatten "inner" lists
[1, 2, 3, 4, 5, 6]

它是一个迭代器,所以你需要迭代它(例如用列表包装它或在循环中使用它)。在内部,它使用迭代方法而不是递归方法,并且它是作为C扩展编写的,因此它可以比纯python方法更快:

>>> %timeit list(deepflatten(L))
12.6 µs ± 298 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit list(deepflatten(L, types=list))
8.7 µs ± 139 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

>>> %timeit list(flatten(L))   # Cristian - Python 3.x approach from https://stackoverflow.com/a/2158532/5393381
86.4 µs ± 4.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit list(flatten(L))   # Josh Lee - https://stackoverflow.com/a/2158522/5393381
107 µs ± 2.99 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit list(genflat(L, list))  # Alex Martelli - https://stackoverflow.com/a/2159079/5393381
23.1 µs ± 710 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

我是iteration_utilities库的作者。

用Python 3迭代解决

此解决方案可用于除str和bytes以外的所有对象。

from collections import Iterable
from collections import Iterator


def flat_iter(obj):
    stack = [obj]
    while stack:
        element = stack.pop()
        if element and isinstance(element, Iterator):
            stack.append(element)
            try:
                stack.append(next(element))
            except StopIteration:
                stack.pop()
        elif isinstance(element, Iterable) and not isinstance(element, (str, bytes)):
            stack.append(iter(element))
        else:
            yield element


tree_list = [[(1,2,3),(4,5,6, (7,8, 'next element is 5')), (5,6), [[[3,4,5],'foo1'],'foo2'],'foo3']]

not_iterable = 10

it1 = flat_iter(tree_list)
it2 = flat_iter(not_iterable)

print(list(it1))
print(list(it2))

[1, 2, 3, 4, 5, 6, 7, 8,下一个元素是5,5,6,3,4,5,‘foo1’,‘foo2’,‘foo3’) [10]