在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是DjangoQuerySet
对象
结论:
感谢所有对此问题做出贡献的人。以下是我所学知识的总结。我还将此作为一个社区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的iterableQuerySet
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)))]))