关于算法:在Python中扁平任意嵌套列表的最快方法是什么?

What is the fastest way to flatten arbitrarily nested lists in Python?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Flattening a shallow list in Python
Flatten (an irregular) list of lists in Python

编辑:问题不是怎么做-这已经在其他问题中讨论过了-问题是,哪种方法最快?

我以前已经找到了解决方案,但我想知道什么是最快的解决方案是扁平包含其他任意长度列表的列表。

例如:

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

将成为:

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

可以有无限多个层次。有些列表对象可以是字符串,不能展平为输出列表中的顺序字符。


下面是一个对字符串友好的递归方法:

1
2
3
4
5
6
7
8
9
10
11
nests = [1, 2, [3, 4, [5],['hi']], [6, [[[7, 'hello']]]]]

def flatten(container):
    for i in container:
        if isinstance(i, (list,tuple)):
            for j in flatten(i):
                yield j
        else:
            yield i

print list(flatten(nests))

返回:

1
[1, 2, 3, 4, 5, 'hi', 6, 7, 'hello']

注意,这并不能保证速度和开销的使用,但是说明了一个递归的解决方案,希望它会有帮助。


它不必是递归的。实际上,由于函数调用中涉及的开销,迭代解决方案通常更快。这是我以前写的一个迭代版本:

1
2
3
4
5
def flatten(items, seqtypes=(list, tuple)):
    for i, x in enumerate(items):
        while i < len(items) and isinstance(items[i], seqtypes):
            items[i:i+1] = items[i]
    return items

还没有测试过这个特定实现的性能,但是它可能没有那么好,因为所有的切片分配,最终可能会移动大量的内存。不过,不要假定它必须是递归的,或者这样写更简单。

这种实现的优点是"就地"扁平列表,而不是像递归解决方案一样返回副本。当内存不足时,这可能很有用。如果您想要一个展平的副本,只需传递一个您想要展平的列表的浅副本:

1
2
flatten(mylist)                # flattens existing list
newlist = flatten(mylist[:])   # makes a flattened copy

另外,该算法不受python递归限制,因为它不是递归的。不过,我敢肯定,这几乎永远不会发挥作用。


此函数应该能够在不使用任何递归的情况下快速扁平嵌套的、不可重复的容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import collections

def flatten(iterable):
    iterator = iter(iterable)
    array, stack = collections.deque(), collections.deque()
    while True:
        try:
            value = next(iterator)
        except StopIteration:
            if not stack:
                return tuple(array)
            iterator = stack.pop()
        else:
            if not isinstance(value, str) \
               and isinstance(value, collections.Iterable):
                stack.append(iterator)
                iterator = iter(value)
            else:
                array.append(value)

大约五年后,我对这件事的看法发生了变化,这可能更好地用于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def main():
    data = [1, 2, [3, 4, [5], []], [6]]
    print(list(flatten(data)))


def flatten(iterable):
    iterator, sentinel, stack = iter(iterable), object(), []
    while True:
        value = next(iterator, sentinel)
        if value is sentinel:
            if not stack:
                break
            iterator = stack.pop()
        elif isinstance(value, str):
            yield value
        else:
            try:
                new_iterator = iter(value)
            except TypeError:
                yield value
            else:
                stack.append(iterator)
                iterator = new_iterator


if __name__ == '__main__':
    main()