在Python中展平浅表列表 有没有一种简单的方法可以用列表理解来平移一个迭代列表,或者失败了,你认为什么是最好的方法来平淡这样一个浅薄的列表,平衡性能和可读性?

在Python中展平浅表列表 有没有一种简单的方法可以用列表理解来平移一个迭代列表,或者失败了,你认为什么是最好的方法来平淡这样一个浅薄的列表,平衡性能和可读性?,python,list-comprehension,Python,List Comprehension,我试图用嵌套的列表理解将这样的列表展平,如下所示: [image for image in menuitem for menuitem in list_of_menuitems] 但是我遇到了名称错误的麻烦,因为名称“menuitem”没有定义。在谷歌搜索和查看堆栈溢出后,我使用reduce语句得到了想要的结果: reduce(list.__add__, map(lambda x: list(x), list_of_menuitems)) 但是这个方法相当不可读,因为我需要在那里调用list

我试图用嵌套的列表理解将这样的列表展平,如下所示:

[image for image in menuitem for menuitem in list_of_menuitems]
但是我遇到了
名称错误
的麻烦,因为
名称“menuitem”没有定义
。在谷歌搜索和查看堆栈溢出后,我使用
reduce
语句得到了想要的结果:

reduce(list.__add__, map(lambda x: list(x), list_of_menuitems))
但是这个方法相当不可读,因为我需要在那里调用
list(x)
,因为x是Django
QuerySet
对象

结论

感谢所有对此问题做出贡献的人。以下是我所学知识的总结。我还将此作为一个社区wiki,以防其他人想要添加或更正这些观察结果

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

>>> reduce(list.__add__, (list(mi) for mi in list_of_menuitems))
这是嵌套列表理解的正确语法(精彩摘要!):

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

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

正如@cdleary所指出的,使用
链来避免*操作符的魔力可能是更好的方式

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

在我的头顶上,你可以消除lambda:

reduce(list.__add__, map(list, [mi.image_set.all() for mi in list_of_menuitems]))
或者甚至取消地图,因为您已经有了一个列表:

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], [])

在我的头顶上,你可以消除lambda:

reduce(list.__add__, map(list, [mi.image_set.all() for mi in list_of_menuitems]))
或者甚至取消地图,因为您已经有了一个列表:

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], [])
那么:

from operator import add
reduce(add, map(lambda x: list(x.image_set.all()), [mi for mi in list_of_menuitems]))
但是,Guido建议不要在一行代码中执行太多操作,因为这样会降低可读性。通过在一行中执行您想要的操作,与在多行中执行您想要的操作相比,性能增益最小(如果有的话)。

关于:

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

但是,Guido建议不要在一行代码中执行太多操作,因为这样会降低可读性。在单行与多行中执行所需的性能最小,如果有的话,

如果您只想遍历数据结构的扁平化版本,不需要索引序列,请考虑./P> 它可以处理任何iterable,其中应该包括Django的iterable
QuerySet
s,这似乎是您在问题中使用的

Edit:无论如何,这可能与reduce一样好,因为reduce将项目复制到正在扩展的列表中的开销相同<代码>链
只有在最后运行
列表(链)
时才会产生此(相同)开销

元编辑:实际上,它比问题的建议解决方案的开销要小,因为当您使用临时列表扩展原始列表时,您会丢弃创建的临时列表


<> > >编辑:<>代码> ItRealStudio.Cu链.FaseRealEdaby避免拆包,你应该使用它来避免<代码> */Cuth-FooT,但显示性能差异微乎其微。

< P>如果你只想遍历数据结构的扁平化版本,不需要索引序列,请考虑./P> 它可以处理任何iterable,其中应该包括Django的iterable
QuerySet
s,这似乎是您在问题中使用的

Edit:无论如何,这可能与reduce一样好,因为reduce将项目复制到正在扩展的列表中的开销相同<代码>链只有在最后运行
列表(链)
时才会产生此(相同)开销

元编辑:实际上,它比问题的建议解决方案的开销要小,因为当您使用临时列表扩展原始列表时,您会丢弃创建的临时列表


编辑:作为
itertools.chain.from\u iterable
避免解包,您应该使用它来避免
*
魔术,但显示的性能差异可以忽略不计。

以下是使用列表理解的正确解决方案(在问题中它们是落后的):

在你的情况下是这样的

[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)

在任何一种情况下,问题的症结都是循环的嵌套。

下面是使用列表理解的正确解决方案(在问题中它们是向后的):

在你的情况下是这样的

[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)
在这两种情况下,gotcha都是
for
循环的嵌套。

你几乎拥有了它!方法是将
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]
你几乎成功了!方法是将
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]

绩效结果。修订

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个项目的两级列表展平1000次

itertools_flatten     0.00554
comprehension_flatten 0.00815
reduce_flatten2       0.01103
reduce_flatten1       0.01404

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

性能结果。修订

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个项目的两级列表展平1000次

itertools_flatten     0.00554
comprehension_flatten 0.00815
reduce_flatten2       0.01103
reduce_flatten1       0.01404

Reduce始终是一个糟糕的选择。

此解决方案适用于任意嵌套深度,而不仅仅是一些(所有?)其他解决方案限制的“列表”深度:

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

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

此解决方案适用于任意嵌套深度-而不仅仅是一些(所有?)其他解决方案限制的“列表”深度:

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
它是允许任意深度嵌套的递归-当然,直到达到最大递归深度…

:你启发我写了一篇tim
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
class SquaredRange(object):
    def __init__(self, n): 
        self.range = range(n)
    def __iter__(self):
        for i in self.range: 
            yield i**2

list_of_list = [SquaredRange(5)]*3
flat_list = []
map(flat_list.extend, list_of_list)
print flat_list
#[0, 1, 4, 9, 16, 0, 1, 4, 9, 16, 0, 1, 4, 9, 16]
l = [['image00', 'image01'], ['image10'], []]
print sum(l,[]) # prints ['image00', 'image01', 'image10']
>>> 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
>>> list2d = ((1,2,3),(4,5,6), (7,), (8,9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop
>>> flattener = re.compile("\'.*?\'")
>>> flattener
<_sre.SRE_Pattern object at 0x10d439ca8>
>>> stred = str(in_list)
>>> outed = flattener.findall(stred)
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
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)
>>> a = [1, [2, 3], [1, [2, 3, [1, [2, 3]]]]]
>>> print(list(flat_list(a)))
[1, 2, 3, 1, 2, 3, 1, 2, 3]
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]
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.]
import morph

list = [[1,2],[3],[5,89],[],[6]]
flattened_list = morph.flatten(list)  # returns [1, 2, 3, 5, 89, 6]
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)))]))