是的,我知道这个主题之前已经讨论过(here、here、here、here),但据我所知,除了一个之外,所有解决方案都在这样的列表中失败:
L = [[[1, 2, 3], [4, 5]], 6]
所需的输出在哪里
[1, 2, 3, 4, 5, 6]
或者甚至更好,一个迭代器。我看到的唯一适用于任意嵌套的解决方案是 in this question:
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)
这是最好的模型吗?我忽略了什么吗?任何问题?
list
旨在同质化)这一事实并不意味着这是 Python 的错,我们需要一个内置函数来完成此类任务
使用生成器函数可以使您的示例更易于阅读并提高性能。
蟒蛇2
使用 2.6 中添加的 Iterable
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
蟒蛇 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
我的解决方案:
import collections
def flatten(x):
if isinstance(x, collections.Iterable):
return [a for i in x for a in flatten(i)]
else:
return [x]
更简洁一些,但几乎相同。
try: iter(x)
来测试它是否可迭代,您可以在不导入任何内容的情况下执行此操作……但我认为不必导入 stdlib 模块是一个值得避免的缺点。
int
类型时才有效
def flatten(x): return [a for i in x for a in flatten(i)] if isinstance(x, collections.Iterable) else [x]
- 但这里的可读性可能是主观的。
if isinstance(x, collections.Iterable) and not isinstance(x, basestring)
collections.Iterable
替换为 list
使用递归和鸭子类型的生成器(针对 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]
for i in flatten(item): yield i
这是我的递归展平功能版本,它处理元组和列表,并允许您输入任何位置参数的组合。返回一个生成器,它按顺序生成整个序列,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]
e
、a
、n
所指的内容,将会很有帮助
n
尝试 args
,为 a
尝试 intermediate
(或更短的 mid
,或者您可能更喜欢 element
),为 e
尝试 result
,所以:flatten = lambda *args: (result for mid in args for result in (flatten(*mid) if isinstance(mid, (tuple, list)) else (mid,)))
compiler.ast.flatten
快得多。伟大、紧凑的代码,适用于任何(我认为)对象类型。
根据@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)
此版本的 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
sets
、dicts
、deques
、listiterators
、generators
、文件句柄和定义了 __iter__
的自定义类都是 collections.Iterable
的实例,但不是 collections.Sequence
。展平 dict
的结果有点不确定,但除此之外,我认为 collections.Iterable
是比 collections.Sequence
更好的默认值。这绝对是更自由的。
collections.Iterable
的一个问题是它包含无限生成器。我已经改变了我的答案处理这种情况。
StopIteration
。此外,看起来 while True: first = next(remainder)
可以替换为 for first in remainder:
。
try-except StopIteration block
中来解决。
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
这是另一个更有趣的答案......
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)
基本上,它将嵌套列表转换为字符串,使用正则表达式去除嵌套语法,然后将结果转换回(扁平)列表。
[['C=64', 'APPLE ]['], ['Amiga', 'Mac', 'ST']]
:) 另一方面,给定一个包含自身的列表,它会比其他答案做得更好, 引发异常而不是循环直到内存耗尽/递归直到耗尽堆栈......
[x for x in c]
只是复制 c
的一种缓慢而冗长的方式,那么您为什么要这样做呢?其次,您的代码显然会将 'APPLE ]['
转换为 'APPLE '
,因为它不处理引用,它只是假定任何括号都是列表括号。
arr_str = str(arr)
然后 [int(s) for s in re.findall(r'\d+', arr_str)]
真的。请参阅github.com/jorgeorpinel/flatten_nested_lists/blob/master/…
尝试创建一个可以在 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
>>>
您可以使用第 3 方软件包 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]
它是一个迭代器,因此您需要对其进行迭代(例如,用 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
库的作者。
这是一个简单的函数,可以展平任意深度的列表。无递归,避免堆栈溢出。
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
collections.deque
而不是复制来提高效率。假设您可以控制嵌套列表的创建。此外,deepcopy
达到了递归限制。
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]
虽然选择了一个优雅且非常 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
请说出这段代码的好坏?
isinstance(i, (tuple, list))
。初始化空变量是我寻找替代代码结构的标志,通常是理解、生成器、递归等。
return type(l)(ret)
也会让您恢复与传入时相同的容器类型。 :)
我更喜欢简单的答案。没有发电机。没有递归或递归限制。只是迭代:
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]
在尝试回答这样的问题时,您确实需要给出您作为解决方案提出的代码的限制。如果只是关于表演,我不会太介意,但作为解决方案提出的大多数代码(包括接受的答案)都无法展平任何深度大于 1000 的列表。
当我说大多数代码时,我的意思是所有使用任何形式的递归(或调用递归的标准库函数)的代码。所有这些代码都失败了,因为对于每个递归调用,(调用)堆栈都会增长一个单位,并且(默认)python 调用堆栈的大小为 1000。
如果您对调用堆栈不太熟悉,那么以下内容可能会有所帮助(否则您可以滚动到实现)。
调用堆栈大小和递归编程(地牢类比)
寻找宝藏并退出
想象一下,你进入一个带有编号房间的巨大地牢,寻找宝藏。你不知道这个地方,但你有一些关于如何找到宝藏的迹象。每个迹象都是一个谜(难度各不相同,但你无法预测它们会有多难)。你决定考虑一下节省时间的策略,你做了两个观察:
找到宝藏很难(很长时间),因为您必须解决(可能很难)谜题才能到达那里。一旦找到宝藏,回到入口可能很容易,你只需要在另一个方向使用相同的路径(虽然这需要一些记忆来回忆你的路径)。
进入地牢时,您会注意到这里有一个小笔记本。你决定用它来写下你在解决一个谜语后离开的每个房间(进入一个新房间时),这样你就可以回到入口处。这是一个天才的想法,你甚至不会花一分钱来实施你的策略。
你进入地牢,成功解开了前 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
的地方。调用堆栈将永远不会从任何东西中释放出来,直到它一个接一个地从所有返回地址中释放出来。
如何避免这个问题?
这实际上很简单:“如果您不知道递归可以走多深,请不要使用递归”。这并非总是如此,因为在某些情况下,Tail Call recursion can be Optimized (TCO)。但在 python 中,情况并非如此,即使是“写得很好”的递归函数也不会不优化堆栈使用。 Guido 有一篇关于这个问题的有趣帖子:Tail Recursion Elimination。
您可以使用一种技术来迭代任何递归函数,我们可以将这种技术称为自带笔记本。例如,在我们的特定情况下,我们只是在探索一个列表,进入一个房间相当于进入一个子列表,您应该问自己的问题是我怎样才能从一个列表返回到它的父列表? 答案并不复杂,重复以下操作,直到 stack
为空:
进入新的子列表时,将当前列表地址和索引压入堆栈(请注意,列表地址+索引也是地址,因此我们只需使用调用堆栈使用的完全相同的技术);每次找到一个项目时,产生它(或将它们添加到列表中);完全探索列表后,使用堆栈返回地址(和索引)返回父列表。
另请注意,这等效于树中的 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,您还“需要”一个堆栈。我之前提出的实现导致具有以下状态(对于 stack
和 flat_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_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
) 的递归调用。出于同样的原因,涉及 str
的 flatten
的解决方案将失败并显示相同的错误消息。
如果你需要测试你的解决方案,你可以使用这个函数来生成一个简单的嵌套列表:
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]]]]]
。
我没有在这里查看所有已经可用的答案,但这是我想出的一个衬里,借鉴了 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]
>>>
def foo():
都是单独的一行。此外,这是非常不可读的。
这是 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 中删除。
完全hacky,但我认为它会工作(取决于你的data_type)
flat_list = ast.literal_eval("[%s]"%re.sub("[\[\]]","",str(the_list)))
我很惊讶没有人想到这一点。该死的递归我没有得到这里的高级人员所做的递归答案。无论如何,这是我对此的尝试。需要注意的是它非常特定于 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]
只需使用 funcy
库:pip install funcy
import funcy
funcy.flatten([[[[1, 1], 1], 2], 3]) # returns generator
funcy.lflatten([[[[1, 1], 1], 2], 3]) # returns list
我是一个愚蠢的人,所以我会给出一个“愚蠢”的解决方案。所有这些递归都伤害了我的大脑。
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()]
您需要知道元素的类型,但我认为这可以概括,并且在速度方面我认为它会更快。
ints
的列表,因此“假设”不适用于此处,如果 OP 另有说明,但他又没有这样做,这是根据以下最简单和最有效的答案之一给了什么。
这是另一种 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(类型数) 来检查是否应该忽略一个元素,当然,任何具有指定忽略类型的派生类型的值都会失败,这就是它使用 str
、unicode
的原因,所以请谨慎使用...
测试:
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 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]
使用 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)
我使用递归来解决任何深度的嵌套列表
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]
current_value = combiner(current_value,each_item) RecursionError: maximum recursion depth exceeded
最简单的方法是通过 pip install morph
使用 morph 库。
代码是:
import morph
list = [[[1, 2, 3], [4, 5]], 6]
flattened_list = morph.flatten(list) # returns [1, 2, 3, 4, 5, 6]
我知道已经有很多很棒的答案,但我想添加一个使用函数式编程方法解决问题的答案。在这个答案中,我使用了双重递归:
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]
我不确定这是否一定更快或更有效,但这就是我所做的:
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]"]
,您将不得不做其他事情。
这是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]
不定期副业成功案例分享
list(flatten(l))
时立即将列表l = ([[chr(i),chr(i-32)] for i in xrange(ord('a'), ord('z')+1)] + range(0,9))
展平的建议。所有其他人,将开始工作并永远花费!collections.Sequence
而不是collections.Iteratable
?for i in flatten(42): print (i)
。这可以通过将isinstance
-test 和 else-clause 移到for el
-loop 之外来解决。 (然后你可以向它扔任何东西,它会从中生成一个扁平化的列表)collections.Iterable
。请改用collections.abc.Iterable
。