是否有一种简单的方法可以通过列表理解来平铺可迭代对象的列表,否则如果不这样做,你们都认为什么是平整此类浅表,平衡性能和可读性的最佳方法?

我试图通过嵌套列表理解来平整这样的列表,如下所示:

[image for image in menuitem for menuitem in list_of_menuitems]


但是我遇到了NameError的麻烦,因为name 'menuitem' is not defined。在谷歌搜索并检查了堆栈溢出之后,我用reduce语句获得了预期的结果: x是Django list(x)对象。

结论:

感谢所有对此问题做出贡献的人。这是我所学的总结。如果其他人想补充或更正这些发现,我也将其设为社区Wiki。

我原来的reduce语句是多余的,最好这样写:
reduce(list.__add__, map(lambda x: list(x), list_of_menuitems))


这是嵌套列表理解的正确语法(出色的摘要dF!):

>>> reduce(list.__add__, (list(mi) for mi in list_of_menuitems))


但是这些方法都不是和使用QuerySet一样有效:

>>> [image for mi in list_of_menuitems for image in mi]


作为@cdleary注意,使用itertools.chain这样避免*运算符魔术可能是更好的样式,例如:

>>> from itertools import chain
>>> list(chain(*list_of_menuitems))


评论

我不明白为什么每个人都在使用map(lambda x:list(x),other)-那不等同于map(list,other)吗?内置列表可以调用...

这是等效的。幸运的是,Prairie Dogg意识到这段代码很丑陋。 :)

@recursive:是的,在您指出了我的reduce语句有多少多余之处之后,我肯定脸红了。我绝对从这个问题中学到了很多,非常感谢大家!

对于所有列表均为空的情况,reduce(list .__ add__,(list(mi.image_set.all())for mi in list_of_menuitems))不正确。它应该为reduce(list .__ add__,(list_of_menuitems中mi的list(mi.image_set.all())),[])

这个问题使stackoverflow.com/q/952914/1206998重复关闭。但是,由于所有与django无关的东西,它尚不清楚。是否应该重写?

#1 楼

如果您只是想遍历数据结构的扁平版本而又不需要可索引的序列,请考虑itertools.chain和company。

>>> list_of_menuitems = [['image00', 'image01'], ['image10'], []]
>>> import itertools
>>> chain = itertools.chain(*list_of_menuitems)
>>> print(list(chain))
['image00', 'image01', 'image10']


它将可用于任何可迭代的事物,其中应包括Django的可迭代QuerySet s,看来您正在使用该问题。

编辑:无论如何,这可能与reduce一样好,因为reduce会将项目复制到要扩展的列表中具有相同的开销。如果最后运行chainlist(chain)仅会产生此(相同)开销。

元编辑:实际上,它比问题的建议解决方案的开销少,因为您丢弃了在创建时创建的临时列表您可以使用临时文件扩展原始文件。

评论


根据此基准测试,使用.extend方法的显式循环是最快的解决方案

– jfs
2014年4月30日在2:02

尚未收到from_iterable的消息。比*更漂亮,如果pythonic更少

–朱尔斯·格林(Jules G.M.)
16-3-29在22:04

还值得强调的是,因为from_iterable避免了拆包,因此可以避免在iterable中有很多(可能无界)项目的问题。如果可迭代的时间足够长,则会耗尽内存。

–歪斜
18-2-27的2:32

#2 楼

您几乎拥有了!进行嵌套列表理解的方法是将for语句的顺序与常规嵌套for语句的顺序相同。

因此,此

for inner_list in outer_list:
    for item in inner_list:
        ...


对应于

[... for inner_list in outer_list for item in inner_list]


所以你想要

[image for menuitem in list_of_menuitems for image in menuitem]


评论


+1,我已经查询了很多次了,这是我所看到的唯一使顺序明确的答案了……也许现在我可以记住了!

–伊兹卡塔
2012年3月20日15:47

我希望我可以再次投票赞成,因为这种思维方式使嵌套列表理解变得更容易理解。

–德里克·丽兹(Derek Litz)
13年6月17日在21:36

而[... for inner_list中的项目,external_list中的inner_list]是Python陷阱:它仅对inner_list的最后一个值重复[... for inner_list中的项目],并重复len(outer_list)次。无用。

–smci
2013年9月14日下午4:47

这种排序真的很奇怪。如果将列表中的i更改为...,将列表中的i更改为...,那么为什么不更改for循环的顺序呢?

–naught101
13年11月26日在6:38

哈哈!我又忘记了它。我猜Guido的大脑和我的大脑在直觉上意见不一致。

–clacke
16 Jun 27'13:33



#3 楼

@ S.Lott:您启发我编写了一个timeit应用程序。

我发现它也会根据分区数(容器列表中的迭代器数)而有所不同-您的评论没有提到三十个项目中有多少个分区。该图在每次运行中将一千个项目弄平,并具有不同数量的分区。项目均匀分布在分区之间。



代码(Python 2.6):

#!/usr/bin/env python2.6

"""Usage: %prog item_count"""

from __future__ import print_function

import collections
import itertools
import operator
from timeit import Timer
import sys

import matplotlib.pyplot as pyplot

def itertools_flatten(iter_lst):
    return list(itertools.chain(*iter_lst))

def itertools_iterable_flatten(iter_iter):
    return list(itertools.chain.from_iterable(iter_iter))

def reduce_flatten(iter_lst):
    return reduce(operator.add, map(list, iter_lst))

def reduce_lambda_flatten(iter_lst):
    return reduce(operator.add, map(lambda x: list(x), [i for i in iter_lst]))

def comprehension_flatten(iter_lst):
    return list(item for iter_ in iter_lst for item in iter_)

METHODS = ['itertools', 'itertools_iterable', 'reduce', 'reduce_lambda',
           'comprehension']

def _time_test_assert(iter_lst):
    """Make sure all methods produce an equivalent value.
    :raise AssertionError: On any non-equivalent value."""
    callables = (globals()[method + '_flatten'] for method in METHODS)
    results = [callable(iter_lst) for callable in callables]
    if not all(result == results[0] for result in results[1:]):
        raise AssertionError

def time_test(partition_count, item_count_per_partition, test_count=10000):
    """Run flatten methods on a list of :param:`partition_count` iterables.
    Normalize results over :param:`test_count` runs.
    :return: Mapping from method to (normalized) microseconds per pass.
    """
    iter_lst = [[dict()] * item_count_per_partition] * partition_count
    print('Partition count:    ', partition_count)
    print('Items per partition:', item_count_per_partition)
    _time_test_assert(iter_lst)
    test_str = 'flatten(%r)' % iter_lst
    result_by_method = {}
    for method in METHODS:
        setup_str = 'from test import %s_flatten as flatten' % method
        t = Timer(test_str, setup_str)
        per_pass = test_count * t.timeit(number=test_count) / test_count
        print('%20s: %.2f usec/pass' % (method, per_pass))
        result_by_method[method] = per_pass
    return result_by_method

if __name__ == '__main__':
    if len(sys.argv) != 2:
        raise ValueError('Need a number of items to flatten')
    item_count = int(sys.argv[1])
    partition_counts = []
    pass_times_by_method = collections.defaultdict(list)
    for partition_count in xrange(1, item_count):
        if item_count % partition_count != 0:
            continue
        items_per_partition = item_count / partition_count
        result_by_method = time_test(partition_count, items_per_partition)
        partition_counts.append(partition_count)
        for method, result in result_by_method.iteritems():
            pass_times_by_method[method].append(result)
    for method, pass_times in pass_times_by_method.iteritems():
        pyplot.plot(partition_counts, pass_times, label=method)
    pyplot.legend()
    pyplot.title('Flattening Comparison for %d Items' % item_count)
    pyplot.xlabel('Number of Partitions')
    pyplot.ylabel('Microseconds')
    pyplot.show()


编辑:决定使其成为社区Wiki。

注意:METHODS应该应该与装饰器一起使用,但是我认为人们用这种方式阅读起来会更容易。

评论


试试sum_flatten = lambda iter_lst:sum(map(list,iter_lst),[])

– jfs
09年1月8日在16:34

或只是求和(列表,[])

–hoju
2009年9月1日下午6:32

@EnTerr建议reduce(operator.iaddstackoverflow.com/questions/3040335/…这是迄今为止最快的(代码:ideone.com/NWThp图片:i403.photobucket.com/albums/pp111/uber_ulrich/p1000.png)

– jfs
10年6月16日在12:43

如果有很多分区,chain.from_iterable()会稍微快些。i403.photobucket.com/albums/pp111/uber_ulrich/p10000.png

– jfs
10年6月16日在13:21

我知道这是一个旧线程,但是我添加了一个从这里得到的方法,该方法使用list.extend,它被证明是最快的。图更新要点

– Mike S
13年4月5日在2:04

#4 楼

sum(list_of_lists, [])会将其弄平。

l = [['image00', 'image01'], ['image10'], []]
print sum(l,[]) # prints ['image00', 'image01', 'image10']


评论


我喜欢!它使我想起使用iter [::-1]而不是sorted(iter,reverse = True)。我不知道这是否是多年来将被视为“不良Python”的事情之一。它给我留下了深刻的印象:TIMTOWTDI解决方案。

– Droogans
11月3日,22:25



#5 楼

此解决方案适用于任意嵌套深度-不仅仅是某些(全部?)其他解决方案限于以下内容的“列表列表”深度:

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


递归允许任意深度嵌套-当然,直到达到最大递归深度为止...

评论


可能值得添加hasattr(el,'__getitem__')以与iter()函数兼容,并内置for-in循环(尽管所有Python序列(带有__getitem__的对象)也是可迭代的(带有__iter__的对象))。

– jfs
09年3月14日在10:56

我期望itertools中已经有类似的东西。是否有使用理解力的类似解决方案?

–Josep Valls
2012年6月18日14:07

这对我来说最有用,因为它不分隔字符串。

– cdhagmann
13年6月27日在20:49

@JosepVallsm不错的解决方案!对于python3,您需要使用str而不是basestring,删除了内置的basestring抽象类型。使用str代替。 str和bytes类型的功能不足以保证共享的基类。 2to3工具(见下文)用str替换每次出现的基本字符串。

–Anu
19年1月12日在19:48

@JosepValls,也可以告诉您为什么类似的方法在输入A = ['str1',[[[[['str2']]]],[['str3'],'str4']上给出错误提示,'str5']并输入A = [1.0,2,'a',(4,),((6,),(8,)),((((8,),(9,)),(( 12,),(10)))]`,但可以很好地解决您的问题!

–Anu
19年1月12日在19:51

#6 楼

在Python 2.6中,使用chain.from_iterable()

>>> from itertools import chain
>>> list(chain.from_iterable(mi.image_set.all() for mi in h.get_image_menu()))


它避免创建中间列表。

#7 楼

性能结果。已修订。

import itertools
def itertools_flatten( aList ):
    return list( itertools.chain(*aList) )

from operator import add
def reduce_flatten1( aList ):
    return reduce(add, map(lambda x: list(x), [mi for mi in aList]))

def reduce_flatten2( aList ):
    return reduce(list.__add__, map(list, aList))

def comprehension_flatten( aList ):
    return list(y for x in aList for y in x)


我将30个项目的2级列表展平了1000次

itertools_flatten     0.00554
comprehension_flatten 0.00815
reduce_flatten2       0.01103
reduce_flatten1       0.01404


减少了总是一个糟糕的选择。

评论


map(lambda x:list(x),[mi for List中的mi]])是一个map(list,一个List)。

– jfs
09年1月2日在20:33

reduce_flatten = lambda list_of_iters:reduce(list .__ add__,map(list,list_of_iters))

– jfs
09年1月2日在20:41

itertools_flatten2 = lambda aList:列表(itertools.chain.from_iterable(aList))

– jfs
09年1月4日在19:40

在2.5.2中没有chain.from_iterable-很抱歉-无法与其他解决方案进行比较。

– S.Lott
09年1月5日,0:29

@recursive版本:sum_flatten = lambda aList:sum(map(list,aList),[])

– jfs
09年1月8日在16:31

#8 楼

似乎对operator.add感到困惑!当您将两个列表加在一起时,正确的术语是concat,而不是相加。 operator.concat是您需要使用的。

如果您正在考虑实用,那么就这么简单::

>>> list2d = ((1,2,3),(4,5,6), (7,), (8,9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)


您会看到减少尊重序列类型,所以当您提供一个元组时,您会得到一个元组。让我们尝试一个列表::

>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(operator.concat, list2d)
[1, 2, 3, 4, 5, 6, 7, 8, 9]


你会得到一个列表。

性能如何::

>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> %timeit list(itertools.chain.from_iterable(list2d))
1000000 loops, best of 3: 1.36 µs per loop


from_iterable非常快!但这并不能与使用concat进行比较。

>>> list2d = ((1,2,3),(4,5,6), (7,), (8,9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop


评论


它可能是一个嵌套级别的最佳解决方案。但这可能是一个过于严格的约束。青年汽车

– LBarret
19年7月23日21:00

#9 楼

在我脑海中,您可以消除lambda:

reduce(list.__add__, map(list, [mi.image_set.all() for mi in list_of_menuitems]))


甚至消除地图,因为您已经有了一个list-comp:

reduce(list.__add__, [list(mi.image_set.all()) for mi in list_of_menuitems])


您也可以将其表示为列表的总和:

sum([list(mi.image_set.all()) for mi in list_of_menuitems], [])


评论


您可以只使用add,我相信sum的第二个参数是多余的。

–丹尼尔
09年1月2日在6:30

这不是多余的。默认值为零,产生TypeError:+不支持的操作数类型:“ int”和“ list”。 IMO sum()比reduce(add,...)更直接

–递归
09年1月2日,下午6:52

#10 楼

这是使用列表推导的正确解决方案(它们在问题中倒退了):

>>> join = lambda it: (y for x in it for y in x)
>>> list(join([[1,2],[3,4,5],[]]))
[1, 2, 3, 4, 5]


对于您而言,它应该是

[image for menuitem in list_of_menuitems for image in menuitem.image_set.all()]


或者您可以使用join并说出

join(menuitem.image_set.all() for menuitem in list_of_menuitems)


无论哪种情况,问题都在于for循环的嵌套。

#11 楼

此版本是一个生成器。如果需要列表,请对其进行调整。

def list_or_tuple(l):
    return isinstance(l,(list,tuple))
## predicate will select the container  to be flattened
## write your own as required
## this one flattens every list/tuple


def flatten(seq,predicate=list_or_tuple):        
    ## recursive generator 
    for i in seq:
        if predicate(seq):
            for j in flatten(i):
                yield j
        else:
            yield i


如果要拼合满足条件的谓词,则可以添加谓词

取自python Cookbook

#12 楼

如果必须平铺具有不可迭代元素或深度大于2的更复杂列表,则可以使用以下函数:

def flat_list(list_to_flat):
    if not isinstance(list_to_flat, list):
        yield list_to_flat
    else:
        for item in list_to_flat:
            yield from flat_list(item)


它将返回生成器对象,您可以可以使用list()函数转换为列表。请注意,yield from语法从python3.3开始可用,但是您可以改用显式迭代。
示例:

>>> a = [1, [2, 3], [1, [2, 3, [1, [2, 3]]]]]
>>> print(list(flat_list(a)))
[1, 2, 3, 1, 2, 3, 1, 2, 3]


评论


此解决方案给出了RECURSION ERROR ON:输入A = ['str1',[[[[[str2']]]],[['str3'],'str4'],'str5'],A = [1.0, 2,'a',[4,],[[6,],[8,]],[[[8,],[9,]],[[12,],[10]]]]。您知道为什么以及如何解决它吗?

–Anu
19年1月12日在20:15

@anu对我来说,在您的示例中没有错误(python 3.7.1)。我不确定为什么它不起作用。

– DartLenin
19年1月12日23:46

我正在使用python3.6,我现在发现了问题,您需要向第一个if条件添加或isinstance(list_to_flat,str),因为它必须防止字符串。您的解决方案非常适合输入A = [1,[[[[[2]]],[[3],4],5],但是当您使用字符串时失败!在python3.7中使用字符串进行过测试吗?

–Anu
19年1月13日,0:28



@anu我在与您提供的完全相同的示例上对其进行了测试。您的第一个示例是使用字符串,并且效果很好。第一个if语句说按原样返回任何非列表项,而不会变平。这也包括字符串,不需要额外的条件。

– DartLenin
19年1月13日在1:06

哦,好的,这可能是由于python版本不同造成的!他们可能已经在3.7中推出了一些更新

–Anu
19年1月13日在1:10

#13 楼

这是使用collectons.Iterable处理多个列表级别的版本:

import collections

def flatten(o, flatten_condition=lambda i: isinstance(i,
               collections.Iterable) and not isinstance(i, str)):
    result = []
    for i in o:
        if flatten_condition(i):
            result.extend(flatten(i, flatten_condition))
        else:
            result.append(i)
    return result


评论


请提出建议,为什么您的解决方案会给出RecursionError:与此输入A相比,超出最大递归深度A = ['image1',[[[[['image2']]],[['image3'],'image4'], 'image5'],当它运行良好并展开该输入A = [1,[2,3],[4,5,[6,[7,8],9]]]

–Anu
19年1月12日在18:29

这是扁平状态的问题。由于字符串是可迭代的,因此将它们扁平化为字符,这些字符本身就是长度为一的字符串,并且由于它们是字符串,因此再次应用相同的逻辑,并创建了无限循环。因此,我创建了一个具有扁平化条件的新版本以进行更多控制。

–皮埃尔·蒂博(Pierre Thibault)
19年1月12日在22:53

大!非常感谢您的澄清,它现在正在工作。我有点理解您的推理,但无法完全理解它。您能否指出我在网络上的任何文章或任何有助于理解其问题的帖子!我了解的是`[['image1']-> ['i','m','a','g','e','1']`,即长度为一的字符串!会进入无限循环,而进入无限循环又是什么?我还不了解的那部分!您能以某种方式提供帮助吗?

–Anu
19年1月13日,0:17

为了使函数变平结束,如果它进入for循环,则需要在某些时候进入else语句。如果将其放入else语句中,则它将开始展开调用堆栈并返回结果。基于以前的版本,因为“ image1”是可迭代的,所以o等于“ image1”,而i等于“ i”。 “ i”也是可迭代的,因此在下一个调用中,o将等于“ i”,而我也将等于“ i”。该函数将再次被调用,从而导致完全相同的状态和一个仅由堆栈溢出中断的无限循环。

–皮埃尔·蒂博(Pierre Thibault)
19年1月13日在5:28

最好使用yield通过结果列表生成项目序列。迭代器可以延迟计算,使用该迭代器的fn可以根据需要消耗序列。

– kopos
19年1月14日在10:28

#14 楼

您尝试过展平吗?
来自matplotlib.cbook.flatten(seq,scalarp =)吗?

l=[[1,2,3],[4,5,6], [7], [8,9]]*33

run("list(flatten(l))")
         3732 function calls (3303 primitive calls) in 0.007 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.007    0.007 <string>:1(<module>)
      429    0.001    0.000    0.001    0.000 cbook.py:475(iterable)
      429    0.002    0.000    0.003    0.000 cbook.py:484(is_string_like)
      429    0.002    0.000    0.006    0.000 cbook.py:565(is_scalar_or_string)
  727/298    0.001    0.000    0.007    0.000 cbook.py:605(flatten)
      429    0.000    0.000    0.001    0.000 core.py:5641(isMaskedArray)
      858    0.001    0.000    0.001    0.000 {isinstance}
      429    0.000    0.000    0.000    0.000 {iter}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



l=[[1,2,3],[4,5,6], [7], [8,9]]*66

run("list(flatten(l))")
         7461 function calls (6603 primitive calls) in 0.007 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.007    0.007 <string>:1(<module>)
      858    0.001    0.000    0.001    0.000 cbook.py:475(iterable)
      858    0.002    0.000    0.003    0.000 cbook.py:484(is_string_like)
      858    0.002    0.000    0.006    0.000 cbook.py:565(is_scalar_or_string)
 1453/595    0.001    0.000    0.007    0.000 cbook.py:605(flatten)
      858    0.000    0.000    0.001    0.000 core.py:5641(isMaskedArray)
     1716    0.001    0.000    0.001    0.000 {isinstance}
      858    0.000    0.000    0.000    0.000 {iter}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



l=[[1,2,3],[4,5,6], [7], [8,9]]*99

run("list(flatten(l))")
         11190 function calls (9903 primitive calls) in 0.010 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.010    0.010 <string>:1(<module>)
     1287    0.002    0.000    0.002    0.000 cbook.py:475(iterable)
     1287    0.003    0.000    0.004    0.000 cbook.py:484(is_string_like)
     1287    0.002    0.000    0.009    0.000 cbook.py:565(is_scalar_or_string)
 2179/892    0.001    0.000    0.010    0.000 cbook.py:605(flatten)
     1287    0.001    0.000    0.001    0.000 core.py:5641(isMaskedArray)
     2574    0.001    0.000    0.001    0.000 {isinstance}
     1287    0.000    0.000    0.000    0.000 {iter}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



l=[[1,2,3],[4,5,6], [7], [8,9]]*132

run("list(flatten(l))")
         14919 function calls (13203 primitive calls) in 0.013 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.013    0.013 <string>:1(<module>)
     1716    0.002    0.000    0.002    0.000 cbook.py:475(iterable)
     1716    0.004    0.000    0.006    0.000 cbook.py:484(is_string_like)
     1716    0.003    0.000    0.011    0.000 cbook.py:565(is_scalar_or_string)
2905/1189    0.002    0.000    0.013    0.000 cbook.py:605(flatten)
     1716    0.001    0.000    0.001    0.000 core.py:5641(isMaskedArray)
     3432    0.001    0.000    0.001    0.000 {isinstance}
     1716    0.001    0.000    0.001    0.000 {iter}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler'


UPDATE
哪个给了我另一个主意:

l=[[1,2,3],[4,5,6], [7], [8,9]]*33

run("flattenlist(l)")
         564 function calls (432 primitive calls) in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    133/1    0.000    0.000    0.000    0.000 <ipython-input-55-39b139bad497>:4(flattenlist)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
      429    0.000    0.000    0.000    0.000 {isinstance}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



l=[[1,2,3],[4,5,6], [7], [8,9]]*66

run("flattenlist(l)")
         1125 function calls (861 primitive calls) in 0.001 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    265/1    0.001    0.000    0.001    0.001 <ipython-input-55-39b139bad497>:4(flattenlist)
        1    0.000    0.000    0.001    0.001 <string>:1(<module>)
      858    0.000    0.000    0.000    0.000 {isinstance}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



l=[[1,2,3],[4,5,6], [7], [8,9]]*99

run("flattenlist(l)")
         1686 function calls (1290 primitive calls) in 0.001 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    397/1    0.001    0.000    0.001    0.001 <ipython-input-55-39b139bad497>:4(flattenlist)
        1    0.000    0.000    0.001    0.001 <string>:1(<module>)
     1287    0.000    0.000    0.000    0.000 {isinstance}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



l=[[1,2,3],[4,5,6], [7], [8,9]]*132

run("flattenlist(l)")
         2247 function calls (1719 primitive calls) in 0.002 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    529/1    0.001    0.000    0.002    0.002 <ipython-input-55-39b139bad497>:4(flattenlist)
        1    0.000    0.000    0.002    0.002 <string>:1(<module>)
     1716    0.001    0.000    0.001    0.000 {isinstance}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



l=[[1,2,3],[4,5,6], [7], [8,9]]*1320

run("flattenlist(l)")
         22443 function calls (17163 primitive calls) in 0.016 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   5281/1    0.011    0.000    0.016    0.016 <ipython-input-55-39b139bad497>:4(flattenlist)
        1    0.000    0.000    0.016    0.016 <string>:1(<module>)
    17160    0.005    0.000    0.005    0.000 {isinstance}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


因此,要测试递归越深,它的有效性:越深?

l=[[1,2,3],[4,5,6], [7], [8,9]]*1320

new=[l]*33

run("flattenlist(new)")
         740589 function calls (566316 primitive calls) in 0.418 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
 174274/1    0.281    0.000    0.417    0.417 <ipython-input-55-39b139bad497>:4(flattenlist)
        1    0.001    0.001    0.418    0.418 <string>:1(<module>)
   566313    0.136    0.000    0.136    0.000 {isinstance}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



new=[l]*66

run("flattenlist(new)")
         1481175 function calls (1132629 primitive calls) in 0.809 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
 348547/1    0.542    0.000    0.807    0.807 <ipython-input-55-39b139bad497>:4(flattenlist)
        1    0.002    0.002    0.809    0.809 <string>:1(<module>)
  1132626    0.266    0.000    0.266    0.000 {isinstance}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



new=[l]*99

run("flattenlist(new)")
         2221761 function calls (1698942 primitive calls) in 1.211 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
 522820/1    0.815    0.000    1.208    1.208 <ipython-input-55-39b139bad497>:4(flattenlist)
        1    0.002    0.002    1.211    1.211 <string>:1(<module>)
  1698939    0.393    0.000    0.393    0.000 {isinstance}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



new=[l]*132

run("flattenlist(new)")
         2962347 function calls (2265255 primitive calls) in 1.630 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
 697093/1    1.091    0.000    1.627    1.627 <ipython-input-55-39b139bad497>:4(flattenlist)
        1    0.003    0.003    1.630    1.630 <string>:1(<module>)
  2265252    0.536    0.000    0.536    0.000 {isinstance}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



new=[l]*1320

run("flattenlist(new)")
         29623443 function calls (22652523 primitive calls) in 16.103 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
6970921/1   10.842    0.000   16.069   16.069 <ipython-input-55-39b139bad497>:4(flattenlist)
        1    0.034    0.034   16.103   16.103 <string>:1(<module>)
 22652520    5.227    0.000    5.227    0.000 {isinstance}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


我会打赌“ flattenlist”,我将长期使用它而不是matploblib,除非我想要一个收益产生器和快速的结果,因为matploblib.cbook中的“ flatten”使用了它,这很快。


这是代码



typ=(list,tuple)


def flattenlist(d):
    thelist = []
    for x in d:
        if not isinstance(x,typ):
            thelist += [x]
        else:
            thelist += flattenlist(x)
    return thelist


#15 楼

根据我的经验,最有效的列表列表平整方法是:

flat_list = []
map(flat_list.extend, list_of_list)


与其他建议的方法进行一些时间比较:

现在,在处理更长的子列表时,效率增益看起来更好:

list_of_list = [range(10)]*1000
%timeit flat_list=[]; map(flat_list.extend, list_of_list)
#10000 loops, best of 3: 119 µs per loop
%timeit flat_list=list(itertools.chain.from_iterable(list_of_list))
#1000 loops, best of 3: 210 µs per loop
%timeit flat_list=[i for sublist in list_of_list for i in sublist]
#1000 loops, best of 3: 525 µs per loop
%timeit flat_list=reduce(list.__add__,list_of_list)
#100 loops, best of 3: 18.1 ms per loop


该方法还可以用于任何迭代对象:

list_of_list = [range(1000)]*10
%timeit flat_list=[]; map(flat_list.extend, list_of_list)
#10000 loops, best of 3: 60.7 µs per loop
%timeit flat_list=list(itertools.chain.from_iterable(list_of_list))
#10000 loops, best of 3: 176 µs per loop


#16 楼

def is_iterable(item):
   return isinstance(item, list) or isinstance(item, tuple)


def flatten(items):
    for i in items:
        if is_iterable(item):
            for m in flatten(i):
                yield m
        else:
            yield i


测试:

print list(flatten2([1.0, 2, 'a', (4,), ((6,), (8,)), (((8,),(9,)), ((12,),(10)))]))


评论


这可能会使字符串变平为单个字符,这可能不是预期的行为吗?

–ericmjl
18年3月23日在12:11

是的,我没有考虑到这种情况。谢谢。

– kopos
18 Mar 24 '18在6:34

@kopos,感谢您的解决方案,但是,我在flatten(i)中遇到m的错误:[上一行重复执行996次以上] RecursionError:输入A上的最大递归深度超过= [1.0,2,'a' ,(4,),(((6,),(8,)),((((8,),(9,)),((12,),(10)))]]和A = ['str1' ,[[[[[str2']]],[['str3'],'str4'],'str5'],但在此输入A = [1,[[[[[[2]]]上工作正常],[[3],4],5]。您知道失败的原因是什么?以及如何解决?有什么建议?

–Anu
19年1月12日在18:47



@kopos,我现在已解决问题!,您需要在if语句中添加一个条件,而不是isinstance(i,str),以防止展平时列表中的字符串!

–Anu
19年1月13日,0:35

@anu:是的,该修复程序有效!但是问题是,我们正在基于hasattr和isinstance标识集合类型。如果我们知道集合节点的类型,则可以为fn定制相同的集合。如果集合是一个集合,我们可能还必须根据函数的表现来调整函数

– kopos
19年1月14日在10:21



#17 楼

该怎么办:

from operator import add
reduce(add, map(lambda x: list(x.image_set.all()), [mi for mi in list_of_menuitems]))


但是,Guido建议不要在一行代码中执行过多的代码,因为这样做会降低可读性。通过在单行或多行中执行所需的操作,可以获得最小的性能提升。

评论


令人难以置信地满足于在一行中完成一些疯狂的工作……但这实际上只是语法suger

–丹尼尔
09年1月2日在6:37

如果我没记错的话,Guido实际上也建议您不要使用reduce和list的理解...但是,我不同意,它们非常有用。

–丹尼尔
09年1月2日,下午6:39

检查此小块与多行功能的性能。我想您会发现这种单线是真正的狗。

– S.Lott
09年1月2日,11:21

可能用lambdas映射是可怕的。每个函数调用产生的开销消耗了代码的生命。我从来没有说过那条特定的线像多线解决方案一样快...;)

–丹尼尔
09年1月3日,下午3:34

#18 楼

pylab提供了flatten:
numpy flatten的链接

评论


注意:Flatten不适用于锯齿状数组。尝试使用hstack代替。

–炖肉
16 Jan 27 '17:48



#19 楼

如果您正在寻找内置的,简单的单缸套,则可以使用:

a = [[1, 2, 3], [4, 5, 6]
b = [i[x] for i in a for x in range(len(i))]
print b


返回

[1, 2, 3, 4, 5, 6]


#20 楼

如果列表中的每个项目都是一个字符串(并且这些字符串中的任何字符串都使用“”而不是“”),则可以使用正则表达式(re模块)

>>> flattener = re.compile("\'.*?\'")
>>> flattener
<_sre.SRE_Pattern object at 0x10d439ca8>
>>> stred = str(in_list)
>>> outed = flattener.findall(stred)


上面的代码将in_list转换为字符串,使用正则表达式在引号(即列表的每个项目)中查找所有子字符串,并将它们作为列表吐出。

#21 楼

一个简单的替代方法是使用numpy的连接,但是它将内容转换为float:

import numpy as np
print np.concatenate([[1,2],[3],[5,89],[],[6]])
# array([  1.,   2.,   3.,   5.,  89.,   6.])
print list(np.concatenate([[1,2],[3],[5,89],[],[6]]))
# [  1.,   2.,   3.,   5.,  89.,   6.]


#22 楼

在Python 2或3中实现此目的的最简单方法是通过pip install morph使用变形库。

代码为:

import morph

list = [[1,2],[3],[5,89],[],[6]]
flattened_list = morph.flatten(list)  # returns [1, 2, 3, 5, 89, 6]


评论


“最简单”是一个很强的词

–cfi
2015年12月17日在12:19

@cfi您建议的答案在Python 2中不起作用,并且从注释中听起来也不是Python 3中甚至可以接受的答案。morph库是一个简单的单功能解决方案,就像您在lodash中使用javascript一样。无论如何,我都编辑了答案以阐明它是适用于Python 2和3的最简单解决方案。

– YPCrumble
2015年12月17日在17:46



我深感抱歉。我的评论有点懒惰,特别是因为您在另一篇文章中指出了我自己的评论。我想指出的一点是,“最简单”是一种超级实现,很难实现。您的建议需要一个外部库,对于某些用户来说,可能很难安装(即使使用venv等)。由于问题是关于“浅”列表和“平衡性能和可读性”,因此您的答案可能会赢得可读性。但是,这种方式在性能上胜出,并且更容易使用,因为它不需要依赖项。

–cfi
15年12月17日在18:27

@cfi是的-我的可能是“懒汉的做法”。对我来说,看到所有这些扁平化方法使我只想找到一个快速的库命令,就像我在morph中发现的那样。关于这个库的好处是它比numpy小得多(我必须使用swapfile在小型服务器实例上安装numpy)。它基本上使用您在第二个注释中描述的功能;另一个选择是让我在代码中将其用作帮助函数。完全没有问题,感谢您指出选项:)。

– YPCrumble
15年12月17日在18:53

#23 楼

在Python 3.4中,您可以执行以下操作:

[*innerlist for innerlist in outer_list]


评论


嗯虽然我对此表示欢迎,但Py3.0早已对此进行了讨论。现在,PEP 448已存在,但仍处于“草稿”模式。相关的错误票证仍在“补丁程序审查”中,但尚未完成。在未将错误标记为“已提交”之前,我会谨慎地抱有希望,并说“您将能够做到”。

–cfi
2013年9月27日在17:33

我明白您的意思,但它是最近一位核心开发人员在2013年Kiwi PyCon上宣布为3.4中的“可接受发布”。仍然不是100%肯定,但我想很有可能。

–裂解酶
2013年9月27日在21:09

我们俩都希望这只是文档在发行之前缺少sw的常规代码;-)

–cfi
2013年9月27日在22:12

SyntaxError:只能将加星标的表达式用作分配目标

–道格
2015年4月9日在21:05

最终的PEP 448不接受此语法

–道格
2015年4月9日在21:13