505

是的,我知道这个主题之前已经讨论过(这里这里这里这里),但据我所知,除了一个之外,所有解决方案都在这样的列表中失败:

L = [[[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

flatten(L)

这是最好的模型吗?我忽略了什么吗?任何问题?

4

48 回答 48

438

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

蟒蛇2

def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, basestring):
            for sub in flatten(el):
                yield sub
        else:
            yield el

我使用了2.6 中添加的Iterable ABC 。

蟒蛇 3

在 Python 3 中,basestring不再存在,但您可以使用strand的元组bytes来获得相同的效果。

运算符一次从生成器yield from返回一个项目。这种用于委派给子生成器的语法是在 3.3 中添加的

from collections.abc import Iterable

def flatten(l):
    for el in l:
        if isinstance(el, Iterable) and not isinstance(el, (str, bytes)):
            yield from flatten(el)
        else:
            yield el
于 2010-01-28T22:35:51.903 回答
60

我的解决方案:

import collections


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

更简洁一些,但几乎相同。

于 2010-01-28T22:34:45.500 回答
44

使用递归和鸭子类型的生成器(针对 Python 3 更新):

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

list(flatten([[[1, 2, 3], [4, 5]], 6]))
>>>[1, 2, 3, 4, 5, 6]
于 2013-01-23T23:04:43.250 回答
38

这是我的递归展平功能版本,它处理元组和列表,并允许您输入任何位置参数的组合。返回一个生成器,它按顺序生成整个序列,arg by arg:

flatten = lambda *n: (e for a in n
    for e in (flatten(*a) if isinstance(a, (tuple, list)) else (a,)))

用法:

l1 = ['a', ['b', ('c', 'd')]]
l2 = [0, 1, (2, 3), [[4, 5, (6, 7, (8,), [9]), 10]], (11,)]
print list(flatten(l1, -2, -1, l2))
['a', 'b', 'c', 'd', -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
于 2011-03-23T17:42:24.403 回答
36

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

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)
于 2010-01-29T00:27:22.303 回答
31

这个版本flatten避免了 python 的递归限制(因此适用于任意深度的嵌套迭代)。它是一个可以处理字符串和任意迭代(甚至无限迭代)的生成器。

import itertools as IT
import collections

def flatten(iterable, ltypes=collections.Iterable):
    remainder = iter(iterable)
    while True:
        first = next(remainder)
        if isinstance(first, ltypes) and not isinstance(first, (str, bytes)):
            remainder = IT.chain(first, remainder)
        else:
            yield first

以下是一些演示其用法的示例:

print(list(IT.islice(flatten(IT.repeat(1)),10)))
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

print(list(IT.islice(flatten(IT.chain(IT.repeat(2,3),
                                       {10,20,30},
                                       'foo bar'.split(),
                                       IT.repeat(1),)),10)))
# [2, 2, 2, 10, 20, 30, 'foo', 'bar', 1, 1]

print(list(flatten([[1,2,[3,4]]])))
# [1, 2, 3, 4]

seq = ([[chr(i),chr(i-32)] for i in range(ord('a'), ord('z')+1)] + list(range(0,9)))
print(list(flatten(seq)))
# ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'f', 'F', 'g', 'G', 'h', 'H',
# 'i', 'I', 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N', 'o', 'O', 'p', 'P',
# 'q', 'Q', 'r', 'R', 's', 'S', 't', 'T', 'u', 'U', 'v', 'V', 'w', 'W', 'x', 'X',
# 'y', 'Y', 'z', 'Z', 0, 1, 2, 3, 4, 5, 6, 7, 8]

虽然flatten可以处理无限生成器,但不能处理无限嵌套:

def infinitely_nested():
    while True:
        yield IT.chain(infinitely_nested(), IT.repeat(1))

print(list(IT.islice(flatten(infinitely_nested()), 10)))
# hangs
于 2010-01-28T22:42:07.743 回答
12
def flatten(xs):
    res = []
    def loop(ys):
        for i in ys:
            if isinstance(i, list):
                loop(i)
            else:
                res.append(i)
    loop(xs)
    return res
于 2011-01-04T04:29:07.627 回答
12

这是另一个更有趣的答案......

import re

def Flatten(TheList):
    a = str(TheList)
    b,_Anon = re.subn(r'[\[,\]]', ' ', a)
    c = b.split()
    d = [int(x) for x in c]

    return(d)

基本上,它将嵌套列表转换为字符串,使用正则表达式去除嵌套语法,然后将结果转换回(扁平)列表。

于 2011-01-14T18:31:33.077 回答
9

尝试创建一个可以在 Python 中展平不规则列表的函数很有趣,但这当然是 Python 的用途(让编程变得有趣)。以下生成器运行良好,但有一些注意事项:

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

它将展平您可能希望单独使用的数据类型(如bytearraybytesstr对象)。此外,代码依赖于从不可迭代对象请求迭代器会引发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但是,当给它一个不可迭代的对象时,新代码将引发 a 。下面显示了新行为的示例。

>>> 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
>>>
于 2013-07-25T20:46:26.470 回答
9

您可以deepflatten从 3rd 方包中使用iteration_utilities

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

它是一个迭代器,因此您需要对其进行迭代(例如,通过将其包装list或在循环中使用它)。在内部,它使用迭代方法而不是递归方法,并且它被编写为 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图书馆的作者。

于 2017-07-29T14:01:42.987 回答
6

虽然选择了一个优雅且非常 Pythonic 的答案,但我会提出我的解决方案,仅供参考:

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

请说出这段代码的好坏?

于 2011-03-07T22:32:07.433 回答
6

这是一个简单的函数,可以展平任意深度的列表。无递归,避免堆栈溢出。

from copy import deepcopy

def flatten_list(nested_list):
    """Flatten an arbitrarily nested list, without recursion (to avoid
    stack overflows). Returns a new list, the original list is unchanged.

    >> list(flatten_list([1, 2, 3, [4], [], [[[[[[[[[5]]]]]]]]]]))
    [1, 2, 3, 4, 5]
    >> list(flatten_list([[1, 2], 3]))
    [1, 2, 3]

    """
    nested_list = deepcopy(nested_list)

    while nested_list:
        sublist = nested_list.pop(0)

        if isinstance(sublist, list):
            nested_list = sublist + nested_list
        else:
            yield sublist
于 2013-12-10T12:56:51.233 回答
6

Pandas 有一个功能可以做到这一点。如您所述,它返回一个迭代器。

In [1]: import pandas
In [2]: pandas.core.common.flatten([[[1, 2, 3], [4, 5]], 6])
Out[2]: <generator object flatten at 0x7f12ade66200>
In [3]: list(pandas.core.common.flatten([[[1, 2, 3], [4, 5]], 6]))
Out[3]: [1, 2, 3, 4, 5, 6]
于 2020-10-24T20:03:15.880 回答
5

在尝试回答这样的问题时,您确实需要给出您作为解决方案提出的代码的限制。如果只是关于表演,我不会太介意,但作为解决方案提出的大多数代码(包括接受的答案)都无法展平任何深度大于 1000 的列表。

当我说大多数代码时,我的意思是所有使用任何形式的递归(或调用递归的标准库函数)的代码。所有这些代码都失败了,因为对于每个递归调用,(调用)堆栈都会增长一个单位,并且(默认)python 调用堆栈的大小为 1000。

如果您对调用堆栈不太熟悉,那么以下内容可能会有所帮助(否则您可以滚动到Implementation)。

调用堆栈大小和递归编程(地牢类比)

寻找宝藏并退出

想象一下,你进入一个带有编号房间的巨大地牢,寻找宝藏。你不知道这个地方,但你有一些关于如何找到宝藏的迹象。每个迹象都是一个谜(难度各不相同,但你无法预测它们会有多难)。你决定考虑一下节省时间的策略,你做了两个观察:

  1. 找到宝藏很难(很长时间),因为您必须解决(可能很难)谜题才能到达那里。
  2. 一旦找到宝藏,回到入口可能很容易,你只需要在另一个方向使用相同的路径(虽然这需要一些记忆来回忆你的路径)。

进入地牢时,您会注意到这里有一个小笔记本。你决定用它来写下你在解决一个谜语后离开的每个房间(进入一个新房间时),这样你就可以回到入口处。这是一个天才的想法,你甚至不会花一分钱来实施你的策略。

你进入地牢,成功解开了前 1001 个谜语,但是你没有计划好,你借来的笔记本里没有空间了。你决定放弃你的任务,因为你宁愿没有宝藏也不愿永远迷失在地牢里(这看起来确实很聪明)。

执行递归程序

基本上,这与寻找宝藏完全一样。地牢是计算机的内存,你现在的目标不是寻找宝藏,而是计算一些函数(找到给定x的f(x))。指示只是帮助您解决f(x)的子例程。您的策略与调用堆栈策略相同,笔记本是堆栈,房间是函数的返回地址:

x = ["over here", "am", "I"]
y = sorted(x) # You're about to enter a room named `sorted`, note down the current room address here so you can return back: 0x4004f4 (that room address looks weird)
# Seems like you went back from your quest using the return address 0x4004f4
# Let's see what you've collected 
print(' '.join(y))

你在地牢中遇到的问题在这里是一样的,调用堆栈的大小是有限的(这里是 1000),因此,如果你输入了太多的函数而没有返回,那么你将填满调用堆栈并出现一个看起来像的错误比如“亲爱的冒险者,很抱歉,你的笔记本满了”RecursionError: maximum recursion depth exceeded。请注意,您不需要递归来填充调用堆栈,但非递归程序不太可能调用 1000 个函数而不返回。同样重要的是要了解,一旦您从函数返回,调用堆栈就会从使用的地址中释放出来(因此名称为“堆栈”,返回地址在进入函数之前被压入并在返回时被拉出)。在简单递归的特殊情况下(一个函数f一次调用自身——一遍又一遍——)你将f一遍又一遍地进入,直到计算完成(直到找到宝藏),然后返回,f直到你回到你f最初调用的地方。调用堆栈将永远不会从任何东西中释放出来,直到它一个接一个地从所有返回地址中释放出来。

如何避免这个问题?

这实际上很简单:“如果您不知道递归可以走多深,请不要使用递归”。这并不总是正确的,因为在某些情况下,尾调用递归可以被优化 (TCO)。但在python中,情况并非如此,即使“写得很好”的递归函数也不会优化堆栈的使用。Guido 有一篇关于这个问题的有趣帖子:Tail Recursion Elimination

有一种技术可以用来使任何递归函数迭代,我们可以称这种技术为自带笔记本。例如,在我们的特定情况下,我们只是在探索一个列表,进入一个房间就相当于进入一个子列表,你应该问自己的问题是我怎样才能从一个列表返回到它的父列表?答案并不复杂,重复以下直到stack为空:

  1. 当进入一个新的子列表时,将当前列表addressindexin a推stack入(注意列表地址+索引也是一个地址,因此我们只使用调用堆栈使用的完全相同的技术);
  2. 每次找到一个项目时,yield它(或将它们添加到列表中);
  3. 完全探索列表后,使用stack return address(and index)返回父列表。

另请注意,这等效于树中的 DFS,其中一些节点是子列表A = [1, 2],而一些节点是简单项:0, 1, 2, 3, 4(for L = [0, [1,2], 3, 4])。树看起来像这样:

                    L
                    |
           -------------------
           |     |     |     |
           0   --A--   3     4
               |   |
               1   2

DFS 遍历前序是:L、0、A、1、2、3、4。请记住,为了实现迭代 DFS,您还“需要”一个堆栈。我之前提出的实现导致具有以下状态(对于stackflat_list):

init.:  stack=[(L, 0)]
**0**:  stack=[(L, 0)],         flat_list=[0]
**A**:  stack=[(L, 1), (A, 0)], flat_list=[0]
**1**:  stack=[(L, 1), (A, 0)], flat_list=[0, 1]
**2**:  stack=[(L, 1), (A, 1)], flat_list=[0, 1, 2]
**3**:  stack=[(L, 2)],         flat_list=[0, 1, 2, 3]
**3**:  stack=[(L, 3)],         flat_list=[0, 1, 2, 3, 4]
return: stack=[],               flat_list=[0, 1, 2, 3, 4]

在此示例中,堆栈最大大小为 2,因为输入列表(以及树)的深度为 2。

执行

对于实现,在 python 中,您可以通过使用迭代器而不是简单列表来简化一点。对(子)迭代器的引用将用于存储子列表返回地址(而不是同时具有列表地址和索引)。这没什么大的区别,但我觉得这更具可读性(而且速度也更快):

def flatten(iterable):
    return list(items_from(iterable))

def items_from(iterable):
    cursor_stack = [iter(iterable)]
    while cursor_stack:
        sub_iterable = cursor_stack[-1]
        try:
            item = next(sub_iterable)
        except StopIteration:   # post-order
            cursor_stack.pop()
            continue
        if is_list_like(item):  # pre-order
            cursor_stack.append(iter(item))
        elif item is not None:
            yield item          # in-order

def is_list_like(item):
    return isinstance(item, list)

另外,请注意在is_list_likeI haveisinstance(item, list)中,可以对其进行更改以处理更多输入类型,在这里我只想拥有最简单的版本,其中 (iterable) 只是一个列表。但你也可以这样做:

def is_list_like(item):
    try:
        iter(item)
        return not isinstance(item, str)  # strings are not lists (hmm...) 
    except TypeError:
        return False

这将字符串视为“简单项目”,因此flatten_iter([["test", "a"], "b])将返回["test", "a", "b"]而不是["t", "e", "s", "t", "a", "b"]。请注意,在这种情况下,iter(item)每个项目都会被调用两次,让我们假设这是读者的练习,以使这个更清晰。

对其他实现的测试和评论

最后,请记住,您不能使用打印无限嵌套列表Lprint(L)因为在内部它将使用对__repr__( RecursionError: maximum recursion depth exceeded while getting the repr of an object) 的递归调用。出于同样的原因,flatten涉及的解决方案str将失败并显示相同的错误消息。

如果你需要测试你的解决方案,你可以使用这个函数来生成一个简单的嵌套列表:

def build_deep_list(depth):
    """Returns a list of the form $l_{depth} = [depth-1, l_{depth-1}]$
    with $depth > 1$ and $l_0 = [0]$.
    """
    sub_list = [0]
    for d in range(1, depth):
        sub_list = [d, sub_list]
    return sub_list

这给出了:build_deep_list(5)>>> [4, [3, [2, [1, [0]]]]]

于 2018-08-02T09:02:19.190 回答
4

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

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]

于 2011-01-13T03:19:39.887 回答
4

我没有在这里查看所有已经可用的答案,但这是我想出的一个衬里,借鉴了 lisp 的 first and rest list 处理方式

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]
>>> 
于 2016-04-08T13:24:02.847 回答
2

这是compiler.ast.flatten2.7.5 中的实现:

def flatten(seq):
    l = []
    for elt in seq:
        t = type(elt)
        if t is tuple or t is list:
            for elt2 in flatten(elt):
                l.append(elt2)
        else:
            l.append(elt)
    return l

有更好、更快的方法(如果你已经到过这里,你已经看过了)

另请注意:

2.6 版后已弃用:编译器包已在 Python 3 中删除。

于 2013-08-27T13:05:57.553 回答
2

完全hacky,但我认为它会工作(取决于你的data_type)

flat_list = ast.literal_eval("[%s]"%re.sub("[\[\]]","",str(the_list)))
于 2014-05-28T17:54:20.563 回答
2

我很惊讶没有人想到这一点。该死的递归我没有得到这里的高级人员所做的递归答案。无论如何,这是我对此的尝试。需要注意的是它非常特定于 OP 的用例

import re

L = [[[1, 2, 3], [4, 5]], 6]
flattened_list = re.sub("[\[\]]", "", str(L)).replace(" ", "").split(",")
new_list = list(map(int, flattened_list))
print(new_list)

输出:

[1, 2, 3, 4, 5, 6]
于 2015-11-14T05:59:47.687 回答
2

只需使用一个funcy库: pip install funcy

import funcy


funcy.flatten([[[[1, 1], 1], 2], 3]) # returns generator
funcy.lflatten([[[[1, 1], 1], 2], 3]) # returns list
于 2019-03-22T18:00:27.833 回答
1

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

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

您需要知道元素的类型,但我认为这可以概括,并且在速度方面我认为它会更快。

于 2011-08-12T08:51:28.970 回答
1

这是另一种 py2 方法,我不确定它是最快的还是最优雅的还是最安全的……

from collections import Iterable
from itertools import imap, repeat, chain


def flat(seqs, ignore=(int, long, float, basestring)):
    return repeat(seqs, 1) if any(imap(isinstance, repeat(seqs), ignore)) or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))

它可以忽略您想要的任何特定(或派生)类型,它返回一个迭代器,因此您可以将其转换为任何特定容器,例如列表、元组、字典或简单地使用它以减少内存占用,无论好坏它可以处理初始的不可迭代对象,例如 int ...

请注意,大多数繁重的工作都是在 C 中完成的,因为据我所知,itertools 是如何实现的,所以虽然它是递归的,但 AFAIK 它不受 python 递归深度的限制,因为函数调用是在 C 中发生的,尽管这个并不意味着你受到内存的限制,特别是在 OS X 中,它的堆栈大小截至今天有一个硬限制(OS X Mavericks)......

有一种稍微快一点的方法,但便携性较差,只有在您可以假设输入的基本元素可以明确确定时才使用它,否则您将获得无限递归,并且 OS X 的堆栈大小有限,将相当快地抛出分段错误......

def flat(seqs, ignore={int, long, float, str, unicode}):
    return repeat(seqs, 1) if type(seqs) in ignore or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))

在这里,我们使用集合来检查类型,因此需要 O(1) 与 O(类型数) 来检查是否应该忽略一个元素,当然,任何具有指定忽略类型的派生类型的值都会失败,这就是它使用的原因strunicode所以谨慎使用它......

测试:

import random

def test_flat(test_size=2000):
    def increase_depth(value, depth=1):
        for func in xrange(depth):
            value = repeat(value, 1)
        return value

    def random_sub_chaining(nested_values):
        for values in nested_values:
            yield chain((values,), chain.from_iterable(imap(next, repeat(nested_values, random.randint(1, 10)))))

    expected_values = zip(xrange(test_size), imap(str, xrange(test_size)))
    nested_values = random_sub_chaining((increase_depth(value, depth) for depth, value in enumerate(expected_values)))
    assert not any(imap(cmp, chain.from_iterable(expected_values), flat(chain(((),), nested_values, ((),)))))

>>> test_flat()
>>> list(flat([[[1, 2, 3], [4, 5]], 6]))
[1, 2, 3, 4, 5, 6]
>>>  

$ uname -a
Darwin Samys-MacBook-Pro.local 13.3.0 Darwin Kernel Version 13.3.0: Tue Jun  3 21:27:35 PDT 2014; root:xnu-2422.110.17~1/RELEASE_X86_64 x86_64
$ python --version
Python 2.7.5
于 2014-08-17T19:52:51.253 回答
1

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

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 之后,很容易使用这个函数做扁平化。或者您可以将其组合成一个功能。我喜欢我的解决方案,因为它可以应用于任何嵌套列表。

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]
于 2014-09-30T22:02:25.540 回答
1

不使用任何库:

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]
于 2014-12-07T06:06:19.670 回答
1

使用itertools.chain

import itertools
from collections import Iterable

def list_flatten(lst):
    flat_lst = []
    for item in itertools.chain(lst):
        if isinstance(item, Iterable):
            item = list_flatten(item)
            flat_lst.extend(item)
        else:
            flat_lst.append(item)
    return flat_lst

或者没有链接:

def flatten(q, final):
    if not q:
        return
    if isinstance(q, list):
        if not isinstance(q[0], list):
            final.append(q[0])
        else:
            flatten(q[0], final)
        flatten(q[1:], final)
    else:
        final.append(q)
于 2015-02-27T02:38:33.637 回答
1

最简单的方法是使用变形库使用pip install morph.

代码是:

import morph

list = [[[1, 2, 3], [4, 5]], 6]
flattened_list = morph.flatten(list)  # returns [1, 2, 3, 4, 5, 6]
于 2015-12-15T20:03:50.063 回答
1

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

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]

我知道它正在使用副作用,但这是我对递归的最好理解

于 2016-04-04T20:38:15.663 回答
1

我知道已经有很多很棒的答案,但我想添加一个使用函数式编程方法解决问题的答案。在这个答案中,我使用了双重递归:

def flatten_list(seq):
    if not seq:
        return []
    elif isinstance(seq[0],list):
        return (flatten_list(seq[0])+flatten_list(seq[1:]))
    else:
        return [seq[0]]+flatten_list(seq[1:])

print(flatten_list([1,2,[3,[4],5],[6,7]]))

输出:

[1, 2, 3, 4, 5, 6, 7]
于 2016-10-03T15:46:27.637 回答
1

这将展平列表或字典(或字典列表或字典等)。它假定值是字符串,并创建一个字符串,将每个项目与分隔符参数连接起来。如果你愿意,你可以使用分隔符将结果拆分为一个列表对象。如果下一个值是列表或字符串,它使用递归。使用 key 参数来判断您是想要字典对象中的键还是值(将键设置为 false)。

def flatten_obj(n_obj, key=True, my_sep=''):
    my_string = ''
    if type(n_obj) == list:
        for val in n_obj:
            my_sep_setter = my_sep if my_string != '' else ''
            if type(val) == list or type(val) == dict:
                my_string += my_sep_setter + flatten_obj(val, key, my_sep)
            else:
                my_string += my_sep_setter + val
    elif type(n_obj) == dict:
        for k, v in n_obj.items():
            my_sep_setter = my_sep if my_string != '' else ''
            d_val = k if key else v
            if type(v) == list or type(v) == dict:
                my_string += my_sep_setter + flatten_obj(v, key, my_sep)
            else:
                my_string += my_sep_setter + d_val
    elif type(n_obj) == str:
        my_sep_setter = my_sep if my_string != '' else ''
        my_string += my_sep_setter + n_obj
        return my_string
    return my_string

print(flatten_obj(['just', 'a', ['test', 'to', 'try'], 'right', 'now', ['or', 'later', 'today'],
                [{'dictionary_test': 'test'}, {'dictionary_test_two': 'later_today'}, 'my power is 9000']], my_sep=', ')

产量:

just, a, test, to, try, right, now, or, later, today, dictionary_test, dictionary_test_two, my power is 9000
于 2017-02-27T01:28:19.293 回答
1

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

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

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

此处的flatten函数将列表转换为字符串,取出所有方括号,将方括号重新附加到两端,然后将其转换回列表。

虽然,如果您知道字符串列表中会有方括号,例如[[1, 2], "[3, 4] and [5]"],您将不得不做其他事情。

于 2017-05-13T02:32:08.023 回答
1

这是python2上扁平化的一个简单实现

flatten=lambda l: reduce(lambda x,y:x+y,map(flatten,l),[]) if isinstance(l,list) else [l]

test=[[1,2,3,[3,4,5],[6,7,[8,9,[10,[11,[12,13,14]]]]]],]
print flatten(test)

#output [1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
于 2018-11-28T15:48:09.030 回答
1

没有递归或嵌套循环。几行。格式良好且易于阅读:

def flatten_deep(arr: list):
    """ Flattens arbitrarily-nested list `arr` into single-dimensional. """

    while arr:
        if isinstance(arr[0], list):  # Checks whether first element is a list
            arr = arr[0] + arr[1:]  # If so, flattens that first element one level
        else:
            yield arr.pop(0)  # Otherwise yield as part of the flat array

flatten_deep(L)

从我自己的代码https://github.com/jorgeorpinel/flatten_nested_lists/blob/master/flatten.py

于 2019-01-22T10:22:40.677 回答
0

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

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

实际上,我是从我不久前编写的一些实践方案代码中改编而来的。

享受!

于 2010-01-31T00:19:36.413 回答
0

我是 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]
于 2010-12-20T23:24:37.670 回答
0

无耻地从我自己对另一个问题的回答中拿走。

这个功能

  • 不使用isinstance,因为它是邪恶的并且会破坏鸭子打字。
  • reduce递归使用。必须有一个答案使用reduce
  • 适用于任意嵌套列表,其元素要么是嵌套列表,要么是原子的非嵌套列表,或原子(受递归限制)。
  • 不 LBYL。
  • 但不适用于包含字符串作为原子的嵌套列表。

下面的代码:

def flattener(left, right):
    try:
        res = reduce(flattener, right, left)
    except TypeError:
        left.append(right)
        res = left
    return res


def flatten(seq):
    return reduce(flattener, seq, [])


>>> nested_list = [0, [1], [[[[2]]]],
                   [3, [], [4, 5]],
                   [6, [7, 8],
                    9, [[[]], 10,
                        []]],
                   11, [], [],
                   [12]]
>>> flatten(nested_list)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
于 2015-10-13T22:41:14.323 回答
0

我们也可以使用python的'type'函数。迭代列表时,我们检查项目是否为列表。如果不是,我们“附加”它,否则我们“扩展”它。这是一个示例代码 -

l=[1,2,[3,4],5,[6,7,8]]
x=[]
for i in l:
    if type(i) is list:
        x.extend(i)
    else:
        x.append(i)
print x

输出:

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

有关 append() 和 extend() 的更多信息,请查看此网站: https ://docs.python.org/2/tutorial/datastructures.html

于 2016-03-22T13:55:38.013 回答
0

Python-3

from collections import Iterable

L = [[[1, 2, 3], [4, 5]], 6,[7,[8,9,[10]]]]

def flatten(thing):
    result = []

    if isinstance(thing, Iterable):
        for item in thing:
            result.extend(flatten(item))
    else:
        result.append(thing)

    return result


flat = flatten(L)
print(flat)
于 2017-02-27T16:01:25.317 回答
0

使用 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]

于 2017-07-20T11:42:27.200 回答
0

从我之前的回答来看,这个函数可以使我能想到的大多数情况变平。我相信这适用于 python 2.3。

def flatten(item, keepcls=(), keepobj=()):
    if not hasattr(item, '__iter__') or isinstance(item, keepcls) or item in keepobj:
        yield item
    else:
        for i in item:
            for j in flatten(i, keepcls, keepobj + (item,)):
                yield j

循环列表

>>> list(flatten([1, 2, [...], 3]))
[1, 2, [1, 2, [...], 3], 3]

深度优先列表

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

嵌套重复列表

>>> list(flatten([[1,2],[1,[1,2]],[1,2]]))
[1, 2, 1, 1, 2, 1, 2]

带有 dicts 的列表(或其他不展平的对象)

>>> list(flatten([1,2, {'a':1, 'b':2}, 'text'], keepcls=(dict, str)))
[1, 2, {'a': 1, 'b': 2}, 'text']

任何迭代

>>> list(flatten((x for x in [1,2, set([3,(4,5),6])])))
[1, 2, 4, 5, 3, 6]

您可能希望保留一些默认类keepcls以使调用函数更简洁。

于 2018-01-15T15:25:30.983 回答
0
def nested_list(depth):
    l = [depth]
    for i in range(depth-1, 0, -1):
        l = [i, l]
    return l

nested_list(10)

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

def Flatten(ul):
    fl = []
    for i in ul:
        if type(i) is list:
            fl += Flatten(i)
        else:
            fl += [i]
    return fl

Flatten(nested_list(10))

[1、2、3、4、5、6、7、8、9、10]

基准测试

l = nested_list(100)

https://stackoverflow.com/a/2158532

导入集合

def 展平(l):
    对于 l 中的 el:
        如果 isinstance(el, collections.Iterable) 而不是 isinstance(el, (str, bytes)):
            flatten(el) 的产量
        别的:
            产量
%%timeit -n 1000
list(flatten(l))

每个循环 320 µs ± 14.3 µs(7 次运行的平均值 ± 标准偏差,每次 1000 个循环)

%%timeit -n 1000
Flatten(l)

每个循环 60 µs ± 10.2 µs(7 次运行的平均值 ± 标准偏差,每次 1000 个循环)

list(flatten(l)) == Flatten(l)

真的

于 2019-10-18T14:10:28.020 回答
0
def flatten(item) -> list:
    if not isinstance(item, list): return item
    return reduce(lambda x, y: x + [y] if not isinstance(y, list) else x + [*flatten(y)], item, [])

两行减少功能。

于 2019-12-04T18:42:33.273 回答
0

我修改了已接受答案的代码并添加了一个关键字max_depth以仅展平到指定的深度。max_depth=0意味着,列表保持原样。也许有人可以使用它:

def flatten(l, __depth=0, max_depth=100):

    for el in l:

        if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):

            __depth += 1
            if __depth <= max_depth:
                yield from flatten(el, __depth=__depth, max_depth=max_depth)
            else:
                yield el
            __depth -= 1

        else:

            yield el

一些例子:

# A
l = []
depth = 5
for i in range(depth):
    el = i
    for j in range(i):
        el = [el]
    l.append(el)
# [0, [1], [[2]], [[[3]]], [[[[4]]]]]

for i in range(depth):
    print(list(flatten_gen(l, max_depth=i)))
# [0, [1], [[2]], [[[3]]], [[[[4]]]]]
# [0,  1,   [2],   [[3]],   [[[4]]]]
# [0,  1,    2,     [3],     [[4]]]
# [0,  1,    2,      3,       [4]]
# [0,  1,    2,      3,        4]


# B
l = [[1, 2], [3, 4, [5, 6, [7, [8, [9]]], 10], 12, [13]], 14, [15]]

for i in range(6):
    print(list(flatten_gen(l, max_depth=i)))
# [[1, 2], [3, 4, [5, 6, [7, [8, [9]]], 10], 12, [13]], 14, [15]]
# [ 1, 2,   3, 4, [5, 6, [7, [8, [9]]], 10], 12, [13],  14,  15]
# [ 1, 2,   3, 4,  5, 6, [7, [8, [9]]], 10,  12,  13,   14,  15]
# [ 1, 2,   3, 4,  5, 6,  7, [8, [9]],  10,  12,  13,   14,  15]
# [ 1, 2,   3, 4,  5, 6,  7,  8, [9],   10,  12,  13,   14,  15]
# [ 1, 2,   3, 4,  5, 6,  7,  8,  9,    10,  12,  13,   14,  15]
于 2019-12-19T16:34:53.370 回答
0

该解决方案基于python的iteration-utilities及其功能 deepflatten

from iteration_utilities import deepflatten
list(deepflatten(test))
于 2021-07-05T13:25:06.817 回答
-1

认为以下可能适用于python 3:

def get_flat_iter(xparent):
    try:
        r = xparent
        if hasattr(xx, '__iter__'):
            iparent = iter(xparent)
            if iparent != xparent:
                r = map(a, xparent)
    finally:
         pass
    return r

irregular_list = [1, [2, [3, 4]]]
flat_list = list(irregular_list)
print(flat_list) # [1, 2, 3, 4]
于 2019-10-25T18:06:39.263 回答
-1

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

感谢 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]
于 2020-08-28T06:25:19.540 回答
-1

我试过在不使用任何库的情况下解决它。只需使用两个嵌套函数即可完成这项工作。

def first(list_to_flatten):
    a = []

    def second(list_to_flatten):
        for i in list_to_flatten:
            if type(i) is not list:
                a.append(i)
            else:
                list_to_flatten = i
                second(list_to_flatten)

    second(list_to_flatten)
    return a

list_to_flatten = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]]
a = first(list_to_flatten)
print(a)

>>> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
于 2021-09-29T13:25:11.567 回答
-2
L2 = [o for k in [[j] if not isinstance(j,list) else j for j in [k for i in [[m] if not 
isinstance(m,list) else m for m in L] for k in i]] for o in k]
于 2013-07-02T13:59:14.033 回答
-3

不使用实例的简单函数

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]
于 2017-11-22T12:26:03.177 回答