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 函数的主要内容,如果未能解决你的问题,请参考以下文章

尝试替换已弃用的 loadnibnamed:owner

用 .on 替换已弃用的 .live [重复]

Snapkit 常量替换已弃用的 .priorityMedium() .priorityHigh() .priorityLow()?

替换 Java Awt 已弃用的方法

什么是已弃用的 getSupportLoaderManager() 的适当替换?

替换已弃用的 `keypress` DOM 事件