扁平化一个不规则的(任意嵌套的)列表列表

192008 次浏览

我的解决方案:

import collections




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

更简洁一点,但基本相同。

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

Python 2

使用2.6中添加的# EYZ0 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

这个版本的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

@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)

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

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代码中改编而来的。

享受吧!

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

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

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

# EYZ0

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

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)

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

虽然已经选择了一个优雅和非常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

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

这是我的递归flatten的函数版本,它可以处理元组和列表,并允许您抛出任何位置参数的混合。返回一个生成器,它会逐参数依次生成整个序列:

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]

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

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

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

使用递归和duck类型的生成器(为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]
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]

尝试在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。下面是新行为的示例。

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

下面是2.7.5中的compiler.ast.flatten实现:

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中已移除编译器包。

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

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

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

flat_list = ast.literal_eval("[%s]"%re.sub("[\[\]]","",str(the_list)))

这是另一种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))

它可以忽略你想要的任何特定(或派生)类型,它返回一个迭代器,所以你可以将它转换为任何特定的容器,如list, tuple, dict或简单地消耗它,以减少内存占用,无论是好是坏,它可以处理初始的不可迭代对象,如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) vs 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

我使用递归解决嵌套列表,具有任意深度

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]

不使用任何库:

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]

使用# EYZ0:

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)

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

这个函数

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

我很惊讶居然没人想到这一点。该死的递归,我没有得到这里的高级人员给出的递归答案。总之,这是我的尝试。警告是它非常特定于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]

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

代码是:

import morph


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

我们也可以使用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()的信息,请访问这个网站: # EYZ0 < / p >

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

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]

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

我没有在这里讨论所有已经可用的答案,但这里有一个我想到的语句,借鉴了lisp的第一和其余列表处理方式

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

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

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]

这将扁平化一个列表或字典(或列表的列表或字典的字典等)。它假设值是字符串,并创建一个字符串,将每个项与分隔符参数连接起来。如果需要,可以使用分隔符将结果拆分为列表对象。如果下一个值是列表或字符串,则使用递归。使用key参数来告诉您是否需要字典对象中的键或值(将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

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)

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

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]"],那么您就必须做其他事情。

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

你可以使用第三方包deepflatten 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库的作者。

不使用实例的简单函数

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]

从我的以前的回答开始,这个函数平坦了我能想到的大多数情况。我相信这适用于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]

# EYZ0:

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

带有字典的列表(或其他物体不要变平)

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

任何iterable

>>> list(flatten((x for x in [1,2, set([3,(4,5),6])])))
[1, 2, 4, 5, 3, 6]
你可能想要在keepcls中保留一些默认类来进行调用

在尝试回答这样一个问题时,您确实需要给出作为解决方案的代码的局限性。如果它只是关于性能,我不会太介意,但作为解决方案提出的大多数代码(包括公认的答案)都不能平坦任何深度大于1000的列表。

当我说大部分代码时,我指的是所有使用任何形式递归的代码(或调用递归的标准库函数)。所有这些代码都失败了,因为每次递归调用,(调用)堆栈都会增加一个单元,(默认)python调用堆栈的大小为1000。

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

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

找到宝藏和出口

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

  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有一篇关于这个问题的有趣帖子:尾递归消除

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

  1. 当输入新的子列表时,在stack中推当前列表addressindex(注意,列表地址+索引也是一个地址,因此我们只是使用与调用堆栈使用的完全相同的技术);
  2. 每次找到一个项目,yield它(或将它们添加到列表中);
  3. 一旦一个列表被完全浏览,使用stack 返回#EYZ1(和index)返回父列表。

还要注意,这相当于树中的DFS,其中一些节点是子列表A = [1, 2],一些是简单的项目:0, 1, 2, 3, 4(对于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中,你可以通过使用迭代器而不是简单的列表来简化实现。对(sub)迭代器的引用将用于存储子列表返回地址(而不是同时拥有列表地址和索引)。这不是很大的区别,但我觉得这样更有可读性(也更快一点):

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_like中我有isinstance(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)在每个项目上被调用两次,让我们假设这是读者的一个练习,让它更清晰。

对其他实现的测试和备注

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

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

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

这是python2上flatten的一个简单实现

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]

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

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

只要使用funcy库: # EYZ0 < / p >
import funcy




funcy.flatten([[[[1, 1], 1], 2], 3]) # returns generator
funcy.lflatten([[[[1, 1], 1], 2], 3]) # returns list
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 < a href = " https://stackoverflow.com/a/2158532 " > < / >

import collections


def flatten(l):
for el in l:
if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):
yield from flatten(el)
else:
yield el
%%timeit -n 1000
list(flatten(l))

320µs±14.3µs / loop(平均±std. dev. 7次运行,每个循环1000次)

%%timeit -n 1000
Flatten(l)

60µs±10.2µs / loop(平均±std. dev. 7次运行,每个循环1000次)

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

真正的

以下的想法可能在python3中工作:

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]
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, [])

双行递减函数。

我修改了接受答案的代码,并添加了关键字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]

大多数答案都使用循环遍历条目。这里我有一个使用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]

熊猫有这样的功能。它返回一个迭代器。

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]

这个解决方案基于python的# EYZ0图书馆和它的函数 deepflatten

from iteration_utilities import deepflatten
list(deepflatten(test))

我试过不使用任何库来解决它。只需使用两个嵌套函数即可。

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]

这个答案的一个更有效的版本:https://stackoverflow.com/a/20495215/8887313

如果您可以控制列表的创建,并且愿意改变它,那么使用deque(而不是pop(0)和列表contatenation)会更有效。

import collections


def flatten_and_consume(nested_deque: collections.deque):
while nested_deque:
elt = nested_deque.popleft()


elt_is_sublist = isinstance(elt, collections.deque)
if elt_is_sublist:
nested_deque.extendleft(reversed(elt))
else:
yield elt

这是我用递归做的:

def flatten(x):
if not any(isinstance(e, list) for e in x):
return x
while type(x[-1]) == int:
x = [x[-1]] + [x[:-1]]
return flatten(x = x + x.pop(-1))

甚至:

def flatten(x):
if not any(isinstance(e, list) for e in x):
return x
return flatten(x = x + x.pop([isinstance(e, list) for e in x].index(1)))