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

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中创建一个可以平化不规则列表的函数是很有趣的,但当然,这就是Python的目的(让编程变得有趣)。以下生成器工作得相当好,但有一些注意事项:

def flatten(iterable):
    try:
        for item in iterable:
            yield from flatten(item)
    except TypeError:
        yield iterable

它将平摊您可能希望保留的数据类型(如bytearray、bytes和str对象)。此外,代码依赖于这样一个事实,即从非可迭代对象请求迭代器会引发TypeError。

>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> def flatten(iterable):
    try:
        for item in iterable:
            yield from flatten(item)
    except TypeError:
        yield iterable


>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>>

编辑:

我不同意之前的实现。问题是你不能将非可迭代对象的东西平展。它令人困惑,给人错误的印象的论点。

>>> list(flatten(123))
[123]
>>>

下面的生成器与第一个生成器几乎相同,但不存在试图将不可迭代对象平展的问题。当给它一个不恰当的论证时,它就会失败。

def flatten(iterable):
    for item in iterable:
        try:
            yield from flatten(item)
        except TypeError:
            yield item

使用提供的列表测试生成器可以正常工作。但是,当给它一个不可迭代对象时,新代码将引发TypeError。下面是新行为的示例。

>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>> list(flatten(123))
Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    list(flatten(123))
  File "<pyshell#27>", line 2, in flatten
    for item in iterable:
TypeError: 'int' object is not iterable
>>>

其他回答

@unutbu的非递归解决方案的生成器版本,由@Andrew在评论中要求:

def genflat(l, ltypes=collections.Sequence):
    l = list(l)
    i = 0
    while i < len(l):
        while isinstance(l[i], ltypes):
            if not l[i]:
                l.pop(i)
                i -= 1
                break
            else:
                l[i:i + 1] = l[i]
        yield l[i]
        i += 1

这个生成器的简化版本:

def genflat(l, ltypes=collections.Sequence):
    l = list(l)
    while l:
        while l and isinstance(l[0], ltypes):
            l[0:1] = l[0]
        if l: yield l.pop(0)

我是一个愚蠢的人,所以我会给出一个“愚蠢”的解决方案。所有的递归都伤了我的大脑。

flattened_list = []
nested_list = [[[1, 2, 3], [4, 5]], 6]

def flatten(nested_list, container):
    for item in nested_list:
        if isintance(item, list):
            flatten(item, container)
        else:
            container.append(item)

>>> flatten(nested_list, flattened_list)
>>> flattened_list
[1, 2, 3, 4, 5, 6]

我知道这是一个副作用但这是我对递归的最好理解

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

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

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

不使用实例的简单函数

L = [[[1, 2, 3], [4, 5]], 6]
l1 = []
def FlattenList(List1):
    for i in range(len(List1)):
        if type(List1[i]) == type([]):
            FlattenList(List1[i])
        else:
            l1.append(List1[i])
    return l1


FlattenList(L)
[1, 2, 3, 4, 5, 6]

不使用任何库:

def flat(l):
    def _flat(l, r):    
        if type(l) is not list:
            r.append(l)
        else:
            for i in l:
                r = r + flat(i)
        return r
    return _flat(l, [])



# example
test = [[1], [[2]], [3], [['a','b','c'] , [['z','x','y']], ['d','f','g']], 4]    
print flat(test) # prints [1, 2, 3, 'a', 'b', 'c', 'z', 'x', 'y', 'd', 'f', 'g', 4]