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

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

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


当前回答

大多数答案都使用循环遍历条目。这里我有一个使用EAFP方法的变体:尝试在输入上获得一个迭代器,如果成功,首先在第一个元素上运行函数,然后在这个迭代器的其余部分上运行。如果你不能得到迭代器,或者它是一个字符串或字节对象:产生元素。

感谢A. Kareem的建议,他发现我的代码非常慢,因为对字符串和字节对象的递归花费了太长时间,这里是我的代码的改进版本。

def flatten(x, it = None):
    try:
        if type(x) in (str, bytes):
            yield x
        else:
            if not it:
                it = iter(x)
            yield from flatten(next(it))
        if type(x) not in (str, bytes):
            yield from flatten(x, it)
    except StopIteration:
        pass
    except Exception:
        yield x

oldlist = [1,[[[["test",3]]]],((4,5,6)),[ bytes("test", encoding="utf-8"),7,[8,9]]]
newlist = [ x for x in flatten(oldlist) ]
print(newlist)
# [1, 'test', 3, 4, 5, 6, b'test', 7, 8, 9]

其他回答

我使用递归解决嵌套列表与任何深度

def combine_nlist(nlist,init=0,combiner=lambda x,y: x+y):
    '''
    apply function: combiner to a nested list element by element(treated as flatten list)
    '''
    current_value=init
    for each_item in nlist:
        if isinstance(each_item,list):
            current_value =combine_nlist(each_item,current_value,combiner)
        else:
            current_value = combiner(current_value,each_item)
    return current_value

所以在我定义函数combine_nlist之后,很容易使用这个函数来做flatting。或者你可以把它组合成一个函数。我喜欢我的解决方案,因为它可以应用于任何嵌套列表。

def flatten_nlist(nlist):
    return combine_nlist(nlist,[],lambda x,y:x+[y])

结果

In [379]: flatten_nlist([1,2,3,[4,5],[6],[[[7],8],9],10])
Out[379]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

最简单的方法是使用pip install morph来使用morph库。

代码是:

import morph

list = [[[1, 2, 3], [4, 5]], 6]
flattened_list = morph.flatten(list)  # returns [1, 2, 3, 4, 5, 6]

我更喜欢简单的答案。没有发电机。没有递归或递归限制。迭代:

def flatten(TheList):
    listIsNested = True

    while listIsNested:                 #outer loop
        keepChecking = False
        Temp = []

        for element in TheList:         #inner loop
            if isinstance(element,list):
                Temp.extend(element)
                keepChecking = True
            else:
                Temp.append(element)

        listIsNested = keepChecking     #determine if outer loop exits
        TheList = Temp[:]

    return TheList

这适用于两个列表:一个内部for循环和一个外部while循环。

内部的for循环遍历列表。如果它发现一个列表元素,它(1)使用list.extend()将第一级嵌套的部分平铺,(2)将keepChecking切换为True。Keepchecking用于控制外部while循环。如果外部循环被设置为true,它将触发内部循环进行另一次传递。

这些传递一直发生,直到没有找到更多的嵌套列表为止。当最终发生传递而没有发现任何传递时,keepChecking永远不会被触发为true,这意味着listIsNested保持为false,外部while循环退出。

然后返回扁平的列表。

一起测试

flatten([1,2,3,4,[100,200,300,[1000,2000,3000]]])

[1, 2, 3, 4, 100, 200, 300, 1000, 2000, 3000]

虽然已经选择了一个优雅和非常python的答案,但我将提出我的解决方案,只是为了回顾:

def flat(l):
    ret = []
    for i in l:
        if isinstance(i, list) or isinstance(i, tuple):
            ret.extend(flat(i))
        else:
            ret.append(i)
    return ret

请告诉我这个代码是好是坏?

使用生成器函数可以使示例更易于阅读并提高性能。

Python 2

使用2.6中添加的Iterable ABC:

from collections import Iterable

def flatten(xs):
    for x in xs:
        if isinstance(x, Iterable) and not isinstance(x, basestring):
            for item in flatten(x):
                yield item
        else:
            yield x

Python 3

在Python 3中,basestring不再是,但元组(str, bytes)具有相同的效果。此外,yield from操作符每次从生成器返回一个项。

from collections.abc import Iterable

def flatten(xs):
    for x in xs:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            yield from flatten(x)
        else:
            yield x