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

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将项目复制到正在扩展的列表中的开销相同<代码>链
只有在最后运行
列表(链)
时才会产生此(相同)开销

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


编辑:作为
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)
在这两种情况下,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]

绩效结果。修订

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

:你启发我写了一个timeit应用程序

我估计它也会根据分区的数量(容器列表中迭代器的数量)而变化——您的评论没有提到30个项目中有多少个分区。这个图在每次运行中都会用不同数量的分区展平1000个项目。这些项均匀地分布在分区中

代码(Python 2.6):

编辑:决定将其设置为社区维基

注意:
方法可能应该通过一个装饰器来积累,但我认为这样阅读会更容易。

在Python 2.6中,使用:

它避免了创建中间列表。

在中,您将能够执行以下操作:

[*innerlist for innerlist in outer_list]
你试过压扁吗? 从哪里来的


此版本是生成器。如果需要列表,请调整它

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中获取的最有效的列表扁平化方法是:

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
这种方法也适用于任何迭代对象:

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]
pylab提供了一个扁平化:

总和(列表中的列表,[])
将使其平坦

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

似乎与
运算符混淆。添加
!当您同时添加两个列表时,正确的术语是
concat
,而不是add<代码>运算符。concat是您需要使用的

如果你想的是功能性的,那么就这么简单::

>>> list2d = ((1,2,3),(4,5,6), (7,), (8,9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)
你看reduce尊重序列类型,所以当你提供一个元组时,你会得到一个元组。让我们试试下面的列表:

>>> 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
从_iterable开始,速度相当快!但与康考特相比,这是不可比拟的

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

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

>flatter=re.compile(“\'.*?\”)
>>>压扁机
>>>stred=str(在列表中)
>>>outed=展平器。findall(拉伸)
上述代码将in_list转换为字符串,使用正则表达式查找引号内的所有子字符串(即
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
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)))]))