Python 3 替换已弃用的 compiler.ast flatten 函数
Posted
技术标签:
【中文标题】Python 3 替换已弃用的 compiler.ast flatten 函数【英文标题】:Python 3 replacement for deprecated compiler.ast flatten function 【发布时间】:2013-04-17 02:29:52 【问题描述】:自deprecation of the compiler package 以来,扁平化嵌套列表的推荐方法是什么?
>>> from compiler.ast import flatten
>>> flatten(["junk",["nested stuff"],[],[[]]])
['junk', 'nested stuff']
我知道列表展平有一些堆栈溢出答案,但我希望 Pythonic 标准包“一种,最好只有一种明显的方式”来做到这一点。
【问题讨论】:
【参考方案1】:只是另一个递归: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']
【讨论】:
【参考方案2】:为递归而构建:Python 3.6
def flatten(lst):
"""Flattens a list of lists"""
return [subelem for elem in lst
for subelem in elem]
在列表中定义你的类型并使用 any 内置检查
【讨论】:
【参考方案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)
【讨论】:
建立这样的列表是一个非常糟糕的解决方案,生成器会更合适。 我会说“非常糟糕的解决方案”有点强。如果嵌套列表相对较小,构建生成器可能会更慢。这取决于需要。我都发布了。 在一个小列表上创建一个生成器可能会慢一些,但是当它很小时,时间差异并不重要。然而,当它很大时,它可能会很好。无论如何,最好只使用生成器,并且它更具可读性。除此之外,hasattr(e, "__iter__")
通常是使用Iterable
ABC 更好地完成检查 - 这样更具可读性。【参考方案4】:
您可以使用funcy 库中的flatten 函数:
from funcy import flatten, isa
flat_list = flatten(your_list)
您还可以明确指定要遵循的值:
# Follow only sets
flat_list = flatten(your_list, follow=isa(set))
如果您想要算法,请查看its implementation。
【讨论】:
我越来越喜欢 funcy 了。感谢您的提示。【参考方案5】:对于任意嵌套的列表没有内置方法,但是像这样......
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]
【讨论】:
-1 - 对列表和元组进行类型检查使得这非常不灵活。 @Lattyware 问题是:“扁平化嵌套列表的推荐方法是什么...” @Lattyware 有关于第二个选项的 cmets 吗? 对不起,我在回答我自己的问题。我的问题是 Python 是一种灵活的语言,而类型检查通常只是一个糟糕的解决方案。提问者可能只考虑列表,但有时会使用一些自定义数据结构,其功能类似于列表,但不适用于您的代码。 Python 是为鸭子类型而设计的,因此仅适用于某些类型的函数,尽管其他类型可以使用,但使用起来非常烦人,而且通常是个坏主意。 我很确定Sequence
需要定义__len__()
,并且可能也测试它是Iterable
。【参考方案6】:
我丑陋的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
【讨论】:
【参考方案7】: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
记得在 2.x 中使用 basestring
而不是 str
,以及 for subel in flatten(el): yield el
而不是 yield from flatten(el)
pre-3.3。
正如 cmets 中所述,我认为这是核选项,并且可能会导致比它解决的问题更多的问题。相反,最好的办法是让您的输出更加规则(例如,包含一项的输出仍然将其作为一项元组提供),并在引入它的地方进行常规展平,而不是在最后全部展平。
这将产生更具逻辑性、可读性和更易于使用的代码。当然,在某些情况下您需要进行这种扁平化(如果数据来自您无法处理的地方,那么您别无选择,只能将其放入结构不良的格式),在这种情况下,可能需要这种解决方案,但总的来说,这可能是个坏主意。
【讨论】:
谢谢。这可能是一个愚蠢的澄清,但你有如何统一它的建议吗?>>> itertools.chain([["junk",["nested stuff"],[],[[]]]]) <itertools.chain object at 0x227dc90>
我通常使用 list(iterable) 来合并它,但如果我在这种情况下这样做,我最终会得到一个非平面列表!
联合?我不太确定这是什么意思。 list(iterable)
是从任意可迭代对象中获取列表的最佳方式。
使用上面的列表,我得到:>>> list(itertools.chain([["junk",["nested stuff"],[],[[]]]]))
[['junk', ['nested stuff'], [], [[]]]]
与 flatten()
得到 ['junk', 'nested stuff']
chain()
将一个可迭代对象作为每个参数,如果您想传入一个可迭代对象,请使用 chain.from_iterable()
。请注意,它(如答案中所述)只会删除一层嵌套。除此之外,制作通用工具并不值得,因为不同的情况会将不同的可迭代对象视为需要展平(例如,字符串)。
抱歉,我可能还是没听明白:>>> list(itertools.chain.from_iterable([["junk",["nested stuff"],[],[[]]]]))
给了我['junk', ['nested stuff'], [], [[]]]
以上是关于Python 3 替换已弃用的 compiler.ast flatten 函数的主要内容,如果未能解决你的问题,请参考以下文章
Snapkit 常量替换已弃用的 .priorityMedium() .priorityHigh() .priorityLow()?