在Python中尽可能高效地构建列表

时间:2012-12-22 00:41:49

标签: python optimization numpy scipy

一般问题:假设您必须在循环中执行此操作,那么在效率方面是否存在构建列表的优选样式?例如,这些选项之一是建立整数列表的最佳选择:

mylist = []

for x, y in mystuff:
  # x, y are strings that need to be
  # added sequentially to list
  mylist.extend([int(x), int(y)])

for x, y in mystuff:
  mylist.append(int(x))
  mylist.append(int(y))

还是其他人?如果相关,可以使用scipy / numpy。感谢。

3 个答案:

答案 0 :(得分:11)

如果你需要像这样进行微观优化,那么了解最快速度的唯一方法就是测试。

简短版本是:appendextend快,而Joran Beasley的建议itertools.chain.from_iterable略快于其中任何一项,但只有当您用map取代import itertools import timeit def makestuff(count): for i in range(count): yield (i, i) def f_extend(mystuff): mylist = [] for x, y in mystuff: mylist.extend([int(x), int(y)]) return mylist def f_append(mystuff): mylist = [] for x, y in mystuff: mylist.append(int(x)) mylist.append(int(y)) return mylist def f_chainmap(mystuff): return list(map(int, itertools.chain(*mystuff))) def f_chaincomp(mystuff): return [int(x) for x in itertools.chain(*mystuff)] def f_chainfrommap(mystuff): return list(map(int, itertools.chain.from_iterable(mystuff))) def f_chainfromcomp(mystuff): return [int(x) for x in itertools.chain.from_iterable(mystuff)] def f_reducecompcomp(mystuff): return [int(x) for x in reduce(operator.iadd, (list(y) for y in mystuff), [])] def f_reducecompmap(mystuff): return [int(x) for x in reduce(operator.iadd, map(list, mystuff), [])] try: import numpy def f_numpy(mystuff): return numpy.array(mystuff).flatten().tolist() def f_numpy2(mystuff): return numpy.array(list(mystuff)).flatten().tolist() except: pass if __name__ == '__main__': import sys main = sys.modules['__main__'] count = int(sys.argv[1]) if len(sys.argv) > 1 else 10000 for f in dir(main): if f.startswith('f_'): func = getattr(main, f) mystuff = makestuff(count) testfunc = lambda: func(mystuff) print('{}: {}'.format(f, timeit.timeit(testfunc, number=count))) 时列表理解。

所以:

map

对于Python 2,我尝试了list版本而没有额外的list,而且速度略快,但仍然没有竞争力。当然,对于Python 3,$ python testlister.py 1000000 f_append: 1.34638285637 f_chaincomp: 2.12710499763 f_chainfromcomp: 1.20806899071 f_chainfrommap: 2.77231812477 f_chainmap: 3.67478609085 f_extend: 1.38338398933 f_numpy: 5.52979397774 f_numpy2: 7.5826470852 f_reducecompcomp: 2.17834687233 f_reducecompmap: 3.16517782211 $ python3 ./testlister.py 1000000 f_append: 0.9949617639649659 f_chaincomp: 2.0521950440015644 f_chainfromcomp: 0.9724521590862423 f_chainfrommap: 2.5558998831082135 f_chainmap: 3.5766013460233808 f_extend: 1.149905970087275 f_reducecompcomp: 2.2112889911513776 f_reducecompmap: 1.9317334480583668 是必需的。

以下是我的时间:

python

我的python3是Apple的股票Python 2.7.2,而itertools是python.org 3.3.0,都是64位,都是OS X 10.8.2,2012年中期MacBook Pro配备2.2GHz i7和4GB。

如果你在POSIX平台上使用32位Python,我过去已经注意到在不太遥远的过去的某个地方,迭代器得到了一个优化,似乎加快了{{{ 1位在64位版本中,但在32位中减慢速度。因此,在这种情况下,您可能会发现append获胜。 (一如既往,在您真正关心优化的平台上进行测试。)

Ashwini Chaudhary与Flattening a shallow list in Python相关联,后者与finding elements in python association lists efficiently有关。我怀疑我的结果和他们的结果之间的区别是2.6.0和2.7.2 / 3.3.0之间的迭代器的改进,但我们明确使用2元素元素而不是更大元素的事实可能更重要

此外,至少有一个答案声称reduce是最快的。原帖中的reduce实现都非常慢,但我能够提出更快的版本。他们仍然没有与appendchain.from_iterable竞争,但他们是在正确的球场。

f_numpy函数是heltonbiker的实现。由于mystuff是一个2D迭代器,这实际上只是生成一个包装迭代器的0D数组,因此所有numpy都可以增加开销。我能够提出一个生成一维迭代器数组的实现,但这甚至更慢,因为现在所有numpy都可以做到经常增加N次开销。我可以获得2D整数数组的唯一方法是首先调用list,就像f_numpy2一样,这使得事情变得更慢。 (公平地说,在其他函数中添加额外的list也会减慢它们的速度,但不会像numpy那样差。)

然而,我很有可能在这里消隐,并且有一种合理的方法可以在这里使用numpy。当然,如果您确定mystuff中的顶级mystuff或每个元素都是listtuple,那么您可以写出更好的内容 - 如果可以的话重新设计你的应用程序,这样你首先得到一个2D numpy.array,而不是一般的序列序列,这将是一个完全不同的故事。但是如果你只是对序列进行一般的2D迭代,那么对于这个用例似乎并不是很好。

答案 1 :(得分:2)

>>> my_list = [[1,2],[3,4]]
>>> flat_list_generator = itertools.chain.from_iterable(my_list)  #flatten (note : its a generator!)
>>> map(int,flat_list_generator ) #map to int type (since OP made them ints explicitly)
[1, 2, 3, 4]

我认为是你想要的

答案 2 :(得分:2)

如果mystuff是一对配对列表,那么您可以使用Numpy执行以下操作:

result = numpy.array(mystuff, dtype=float).flatten()

或者可选择将其列为一个列表:

result = numpy.array(mystuff, dtype=float).flatten().tolist()

从我的定性经验来看,这些数组创建程序非常快。