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

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的新手,有lisp的背景。这是我想出的(检查lulz的var名称):

def flatten(lst):
    if lst:
        car,*cdr=lst
        if isinstance(car,(list,tuple)):
            if cdr: return flatten(car) + flatten(cdr)
            return flatten(car)
        if cdr: return [car] + flatten(cdr)
        return [car]

似乎有用。测试:

flatten((1,2,3,(4,5,6,(7,8,(((1,2)))))))

返回:

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

其他回答

你可以使用第三方包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的新手,有lisp的背景。这是我想出的(检查lulz的var名称):

def flatten(lst):
    if lst:
        car,*cdr=lst
        if isinstance(car,(list,tuple)):
            if cdr: return flatten(car) + flatten(cdr)
            return flatten(car)
        if cdr: return [car] + flatten(cdr)
        return [car]

似乎有用。测试:

flatten((1,2,3,(4,5,6,(7,8,(((1,2)))))))

返回:

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

我在这里没有看到类似的帖子,只是从一个关于同一主题的封闭问题中得到的,但为什么不做这样的事情呢(如果你知道你想拆分的列表类型):

>>> a = [1, 2, 3, 5, 10, [1, 25, 11, [1, 0]]]    
>>> g = str(a).replace('[', '').replace(']', '')    
>>> b = [int(x) for x in g.split(',') if x.strip()]

你需要知道元素的类型,但我认为这是可以推广的,就速度而言,我认为它会更快。

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

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]

如果你喜欢递归,这可能是你感兴趣的解决方案:

def f(E):
    if E==[]: 
        return []
    elif type(E) != list: 
        return [E]
    else:
        a = f(E[0])
        b = f(E[1:])
        a.extend(b)
        return a

实际上,这是从我以前写的一些Scheme代码中改编而来的。

享受吧!