在Python中展开浅层列表
这个问题在这里已经有了答案:
如果您只是在遍历数据结构的扁平版本并且不需要可索引序列,请考虑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
,它似乎是您在问题中使用的。
编辑:这可能与减少一样好,因为减少将有相同的开销将项目复制到正在扩展的列表中。 chain
只会在最后运行list(chain)
时产生此(相同)开销。
Meta-Edit:实际上,这比问题提出的解决方案花费更少,因为当您使用临时扩展原始文件时,会丢弃您创建的临时列表。
编辑:由于JF塞巴斯蒂安说itertools.chain.from_iterable
避免拆包,你应该使用它来避免*
魔法,但timeit应用程序显示可以忽略不计的性能差异。
你几乎拥有它! 执行嵌套列表解析的方法是将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]
@ S.Lott:你激励我写一个timeit应用程序。
我想它也会根据分区数量(容器列表中的迭代器数量)而有所不同 - 您的评论没有提及这30个项目有多少个分区。 这种情节在每次运行中都会压扁一千个项目,分区数量也不尽相同。 这些项目均匀分布在分区之间。
代码(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
应该可以与装饰器一起累积,但我认为人们用这种方式阅读会更容易。