10

自编译器包弃用以来,扁平化嵌套列表的推荐方法是什么?

>>> from compiler.ast import flatten
>>> flatten(["junk",["nested stuff"],[],[[]]])
['junk', 'nested stuff']

我知道列表展平有一些堆栈溢出答案,但我希望 Pythonic 标准包“一种,最好只有一种明显的方法”来做到这一点。

4

7 回答 7

12

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 之前使用。

正如评论中所指出的,我认为这是核选项,并且可能会导致比它解决的问题更多的问题。相反,最好的办法是让您的输出更加规则(例如,包含一项的输出仍然将其作为一项元组提供),并在引入它的地方进行常规展平,而不是在最后全部展平。

这将产生更具逻辑性、可读性和更易于使用的代码。当然,在某些情况下您需要进行这种扁平化(如果数据来自您无法处理的地方,那么您别无选择,只能采用结构不良的格式),在这种情况下,可能需要这种解决方案,但总的来说,这可能是个坏主意。

于 2013-04-23T18:39:16.147 回答
6

您声明的函数采用嵌套列表并将其展平为新列表。

要将任意嵌套的列表展平为新列表,这可以在 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)   
于 2013-04-23T18:49:09.150 回答
4

您可以使用来自funcy库的flatten函数:

from funcy import flatten, isa
flat_list = flatten(your_list)

您还可以明确指定要遵循的值:

# Follow only sets
flat_list = flatten(your_list, follow=isa(set))

如果你想要一个算法,可以看看它的实现。

于 2014-06-04T17:20:23.713 回答
1

对于具有任意嵌套的列表没有内置方法,但是像这样......

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]
于 2013-04-23T18:49:52.307 回答
1

我丑陋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
于 2013-04-23T19:48:06.173 回答
0

只是另一个递归: 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']
于 2020-01-23T00:37:48.130 回答
0

为递归而构建:Python 3.6

def flatten(lst):
    """Flattens a list of lists"""
    return [subelem for elem in lst 
                    for subelem in elem]

在列表中定义您的类型并使用 any 内置检查

于 2018-06-26T20:22:36.697 回答