自编译器包弃用以来,扁平化嵌套列表的推荐方法是什么?
>>> from compiler.ast import flatten
>>> flatten(["junk",["nested stuff"],[],[[]]])
['junk', 'nested stuff']
我知道列表展平有一些堆栈溢出答案,但我希望 Pythonic 标准包“一种,最好只有一种明显的方法”来做到这一点。
自编译器包弃用以来,扁平化嵌套列表的推荐方法是什么?
>>> from compiler.ast import flatten
>>> flatten(["junk",["nested stuff"],[],[[]]])
['junk', 'nested stuff']
我知道列表展平有一些堆栈溢出答案,但我希望 Pythonic 标准包“一种,最好只有一种明显的方法”来做到这一点。
itertools.chain
是扁平化任何嵌套可迭代一级的最佳解决方案 - 与任何纯 python 解决方案相比,它都非常高效。
也就是说,它适用于所有可迭代对象,因此例如,如果您想避免它使字符串变平,则需要进行一些检查。
同样,它不会神奇地变平到任意深度。也就是说,通常不需要这样的通用解决方案 - 相反,最好保持数据结构化,这样就不需要以这种方式展平。
编辑:我认为如果必须进行任意展平,这是最好的方法:
import collections
def flatten(iterable):
for el in iterable:
if isinstance(el, collections.Iterable) and not isinstance(el, str):
yield from flatten(el)
else:
yield el
请记住basestring
在 2.x中使用str
,而for subel in flatten(el): yield el
不是在yield from flatten(el)
3.3 之前使用。
正如评论中所指出的,我认为这是核选项,并且可能会导致比它解决的问题更多的问题。相反,最好的办法是让您的输出更加规则(例如,包含一项的输出仍然将其作为一项元组提供),并在引入它的地方进行常规展平,而不是在最后全部展平。
这将产生更具逻辑性、可读性和更易于使用的代码。当然,在某些情况下您需要进行这种扁平化(如果数据来自您无法处理的地方,那么您别无选择,只能采用结构不良的格式),在这种情况下,可能需要这种解决方案,但总的来说,这可能是个坏主意。
您声明的函数采用嵌套列表并将其展平为新列表。
要将任意嵌套的列表展平为新列表,这可以在 Python 3 上按预期工作:
import collections
def flatten(x):
result = []
for el in x:
if isinstance(x, collections.Iterable) and not isinstance(el, str):
result.extend(flatten(el))
else:
result.append(el)
return result
print(flatten(["junk",["nested stuff"],[],[[]]]))
印刷:
['junk', 'nested stuff']
如果你想要一个做同样事情的生成器:
def flat_gen(x):
def iselement(e):
return not(isinstance(e, collections.Iterable) and not isinstance(e, str))
for el in x:
if iselement(el):
yield el
else:
for sub in flat_gen(el): yield sub
print(list(flat_gen(["junk",["nested stuff"],[],[[[],['deep']]]])))
# ['junk', 'nested stuff', 'deep']
对于 Python 3.3 及更高版本,使用yield from而不是循环:
def flat_gen(x):
def iselement(e):
return not(isinstance(e, collections.Iterable) and not isinstance(e, str))
for el in x:
if iselement(el):
yield el
else:
yield from flat_gen(el)
您可以使用来自funcy库的flatten函数:
from funcy import flatten, isa
flat_list = flatten(your_list)
您还可以明确指定要遵循的值:
# Follow only sets
flat_list = flatten(your_list, follow=isa(set))
如果你想要一个算法,可以看看它的实现。
对于具有任意嵌套的列表没有内置方法,但是像这样......
def flatten(l):
for i in l:
if isinstance(i, (list, tuple)):
for ii in flatten(i):
yield ii
else:
yield i
>>> l = ["junk",["nested stuff"],[],[[]]]
>>> list(flatten(l))
['junk', 'nested stuff']
...将适用于列表和元组。如果您想支持可以在for item in object
表达式中使用的任何对象,那么最好使用像这样的鸭子类型......
def flatten(l):
for i in l:
if isinstance(i, (str, bytes)):
yield i
else:
try:
for ii in flatten(i):
yield ii
except TypeError:
yield i
>>> l = ["junk",["nested stuff"],[],[[]]]
>>> list(flatten(l))
['junk', 'nested stuff']
...这比检查更健壮isinstance(el, Iterable)
,因为它无法处理某些情况,例如这种情况...
class Range10:
def __getitem__(self, index):
if index >= 10:
raise IndexError
return index
>>> import collections
>>> r10 = Range10()
>>> isinstance(r10, collections.Iterable)
False
>>> list(Range10())
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
我丑陋while-chain
的解决方案,只是为了好玩:
from collections import Iterable
from itertools import chain
def flatten3(seq, exclude=(str,)):
sub = iter(seq)
try:
while sub:
while True:
j = next(sub)
if not isinstance(j, Iterable) or isinstance(j, exclude):
yield j
else:
sub = chain(j, sub)
break
except StopIteration:
return
只是另一个递归: Python 3.6 。适用于复杂的列表/字符串/数字的嵌套列表和所有的组合:
def list_flattener(nestedList):
nonListElems=[]
listElems=[]
nestedListCounter=0
for nc in range(len(nestedList)):
if type(nestedList[nc])==list:
nestedListCounter+=1
listElems=nestedList[nc]+listElems
else:nonListElems.append(nestedList[nc])
if nestedListCounter==0: return (nestedList)
else:
nestedList=listElems+nonListElems
return list_flattener(nestedList)
请参见下面的示例:
>>> nestedList=['arash',[[1,'anotherOne',[12,'stringX',['kave']]], 'stringY']]
>>> list_flattener(nestedList)
['kave', 12, 'stringX', 1, 'anotherOne', 'stringY', 'arash']
为递归而构建:Python 3.6
def flatten(lst):
"""Flattens a list of lists"""
return [subelem for elem in lst
for subelem in elem]
在列表中定义您的类型并使用 any 内置检查