是否有一种简单的方法可以用列表理解来扁平化一个可迭代对象列表,或者如果没有,你们都认为什么是扁平化这样一个浅列表的最好方法,平衡性能和可读性?

我尝试用一个嵌套的列表理解来扁平化这样一个列表,就像这样:

[image for image in menuitem for menuitem in list_of_menuitems]

但是我遇到了NameError的麻烦,因为名字‘menuitem’没有定义。在google和Stack Overflow上搜索之后,我用一个reduce语句得到了想要的结果:

reduce(list.__add__, map(lambda x: list(x), list_of_menuitems))

但是这个方法是相当不可读的,因为我需要调用list(x)因为x是Django QuerySet对象。

结论:

感谢每个为这个问题做出贡献的人。以下是我所学到的一份总结。我也把它变成了一个社区维基,以防其他人想要添加或纠正这些观察。

我原来的reduce语句是多余的,最好这样写:

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

这是嵌套列表理解的正确语法(聪明的总结dF!)

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

但这两种方法都不如使用itertools.chain有效:

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

正如@cdleary所指出的那样,使用chain.from_iterable来避免*操作符魔法可能是更好的风格,就像这样:

>>> chain = itertools.chain.from_iterable([[1,2],[3],[5,89],[],[6]])
>>> print(list(chain))
>>> [1, 2, 3, 5, 89, 6]

当前回答

如果您只是希望迭代一个扁平的数据结构版本,并且不需要可索引序列,请考虑itertools。连锁店和公司。

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

它可以在任何可迭代的东西上工作,其中应该包括Django的可迭代QuerySets,它似乎是你在问题中使用的。

编辑:无论如何,这可能和reduce一样好,因为reduce将有相同的开销将项复制到正在扩展的列表中。如果你在最后运行list(Chain), Chain只会引起这种(相同的)开销。

Meta-Edit:实际上,它的开销比问题提出的解决方案要少,因为当您使用临时列表扩展原始列表时,会丢弃您创建的临时列表。

编辑:正如J.F.塞巴斯蒂安说itertools.chain.from_iterable避免解包,你应该使用它来避免*魔术,但timeit应用程序显示可以忽略的性能差异。

其他回答

下面是使用列表推导式的正确解决方案(它们在这个问题中是落后的):

>>> 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循环的嵌套。

一个简单的替代方法是使用numpy的concatate,但它将内容转换为浮点数:

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.]

如果您只是希望迭代一个扁平的数据结构版本,并且不需要可索引序列,请考虑itertools。连锁店和公司。

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

它可以在任何可迭代的东西上工作,其中应该包括Django的可迭代QuerySets,它似乎是你在问题中使用的。

编辑:无论如何,这可能和reduce一样好,因为reduce将有相同的开销将项复制到正在扩展的列表中。如果你在最后运行list(Chain), Chain只会引起这种(相同的)开销。

Meta-Edit:实际上,它的开销比问题提出的解决方案要少,因为当您使用临时列表扩展原始列表时,会丢弃您创建的临时列表。

编辑:正如J.F.塞巴斯蒂安说itertools.chain.from_iterable避免解包,你应该使用它来避免*魔术,但timeit应用程序显示可以忽略的性能差异。

你就快成功了!进行嵌套列表推导的方法是将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]

下面是一个使用集合处理多层列表的版本。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