Python でいつジェネレーター式を使用し、いつリスト内包表記を使用する必要がありますか?
# Generator expression
(x*2 for x in range(256))
# List comprehension
[x*2 for x in range(256)]
Python でいつジェネレーター式を使用し、いつリスト内包表記を使用する必要がありますか?
# Generator expression
(x*2 for x in range(256))
# List comprehension
[x*2 for x in range(256)]
ジョンの答えは良いです(何かを複数回繰り返したい場合は、リスト内包表記の方が優れています)。ただし、リスト メソッドのいずれかを使用する場合は、リストを使用する必要があることにも注意してください。たとえば、次のコードは機能しません。
def gen():
return (something for something in get_some_stuff())
print gen()[:2] # generators don't support indexing or slicing
print [5,6] + gen() # generators can't be added to lists
基本的に、繰り返しを 1 回だけ行う場合は、ジェネレーター式を使用します。生成された結果を保存して使用したい場合は、おそらくリスト内包表記を使用したほうがよいでしょう。
どちらかを選択する最も一般的な理由はパフォーマンスであるため、私のアドバイスは、気にせずに 1 つを選択することです。プログラムの実行が遅すぎることに気付いた場合は、そのときだけ戻ってコードのチューニングについて心配する必要があります。
ジェネレーター式またはリスト内包表記を反復すると、同じことが行われます。ただし、リスト内包表記は最初にメモリ内にリスト全体を作成しますが、ジェネレータ式はオンザフライでアイテムを作成するため、非常に大きな (そして無限の!) シーケンスにも使用できます。
結果を複数回反復する必要がある場合、または速度が最優先される場合は、リスト内包表記を使用します。範囲が大きいまたは無限のジェネレータ式を使用します。
詳細については、ジェネレータ式とリスト内包表記を参照してください。
ジェネレーター式の利点は、リスト全体を一度に作成しないため、メモリの使用量が少ないことです。結果を合計したり、結果から dict を作成したりするなど、リストが仲介である場合は、ジェネレータ式を使用するのが最適です。
例えば:
sum(x*2 for x in xrange(256))
dict( (k, some_func(k)) for k in some_list_of_keys )
そこの利点は、リストが完全に生成されないため、使用されるメモリがほとんどないことです (また、より高速になるはずです)。
ただし、目的の最終製品がリストの場合は、リスト内包表記を使用する必要があります。生成されたリストが必要なので、ジェネレータ式を使用してメモリを保存するつもりはありません。また、並べ替えや反転などのリスト関数を使用できるという利点もあります。
例えば:
reversed( [x*2 for x in xrange(256)] )
(リストなどの) 可変オブジェクトからジェネレーターを作成する場合、ジェネレーターは、ジェネレーターの作成時ではなく、ジェネレーターの使用時にリストの状態で評価されることに注意してください。
>>> mylist = ["a", "b", "c"]
>>> gen = (elem + "1" for elem in mylist)
>>> mylist.clear()
>>> for x in gen: print (x)
# nothing
リストが変更される (またはそのリスト内の変更可能なオブジェクト) 可能性があるが、ジェネレーターの作成時に状態が必要な場合は、代わりにリスト内包表記を使用する必要があります。
itertoolsからtee関数を使用しない場合があります。これは、独立して使用できる同じジェネレーターに対して複数のイテレーターを返します。
Hadoop Mincemeat モジュールを使用しています。これは、メモを取るのに最適な例だと思います。
import mincemeat
def mapfn(k,v):
for w in v:
yield 'sum',w
#yield 'count',1
def reducefn(k,v):
r1=sum(v)
r2=len(v)
print r2
m=r1/r2
std=0
for i in range(r2):
std+=pow(abs(v[i]-m),2)
res=pow((std/r2),0.5)
return r1,r2,res
ここで、ジェネレーターはテキスト ファイル (最大 15 GB) から数値を取得し、Hadoop の map-reduce を使用してそれらの数値に簡単な計算を適用します。もしyield関数を使わずにリスト内包表記を使っていたら、合計と平均を計算するのにもっと長い時間がかかっていたでしょう(スペースの複雑さは言うまでもありません)。
Hadoop は、ジェネレーターのすべての利点を活用する好例です。