2つの実装を試しました。1つはジェネレーターを使用し、もう1つはジェネレーターを使用しません。2.7でテストしたのでrange
、イテレータではなくリストを返します。
これが実装です
ジェネレーターの使用
def foo1():
data = ((a,b) for a in (i*i for i in xrange(1,101) if i%2) for b in [1,2,3,4,5] if a+b > 40)
return list(data)
ジェネレータなし
def foo2():
result=[]
for i in range(1,101):
if i%2:
i=i*i
for j in [1,2,3,4,5]:
if i+j > 40:
result+=[(i,j)]
return result
リストを追加しないように両方を混合する
def foo3():
data=[(a,b) for a in (i*i for i in range(1,101)) for b in [1,2,3,4,5] if a+b > 40]
return data
一時リストの作成
def foo4():
data=[(a,b) for a in [i*i for i in range(1,101)] for b in [1,2,3,4,5] if a+b > 40]
return data
これが私の結果です
>>> t1=timeit.Timer("foo1()","from __main__ import foo1")
>>> t2=timeit.Timer("foo2()","from __main__ import foo2")
>>> t3=timeit.Timer("foo3()","from __main__ import foo3")
>>> t4=timeit.Timer("foo4()","from __main__ import foo4")
>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=10000)/10000)
100.95 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=10000)/10000)
158.90 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t3.timeit(number=10000)/10000)
130.02 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t4.timeit(number=10000)/10000)
133.68 usec/pass
>>>
結論:
ジェネレータ式は強力であり、さらに大幅に最適化できます。最も遅い例でわかるようにfoo2
、パフォーマンスを損なう単一のリストを追加するのに苦労しました。foo3
ほぼ同じ時間でfoo4
あるため、一時リストの作成はボトルネックではなかったようです。これは、反復全体で1回だけ作成されたためです。ジェネレーターがないと、リストの追加や一時リストの作成など、パフォーマンスの問題がすぐに発生します。そのため、これらのパフォーマンスのボトルネックを克服するために、遅延評価が必要になりました。