Pythonジェネレータは何ですか?

2846 ワード

前言
生成器はPython初級開発者の最も理解しにくい概念の一つで、Pythonプログラミングの中の高級な技能だと思われますが、各種のプロジェクトの中で随所にジェネレータの姿が見られます。それを理解したり、使ったり、恋に落ちたりしてはいけません。
ジェネレータというと、どうしてもサブジェネレータを引っ張り出すことが避けられません。ジェネレータとは行為上、ローズマリーと非常に類似したオブジェクトです。ディケンサをAndroidシステムにたとえると、ジェネレータはiOSです。二つの機能は同じですが、生成器はもっと優雅です。
ローズマリーとは何ですか
名前の通り、ディケンサは反復動作(forループ)の対象であり、リストのように繰り返し取得することができます。それぞれの要素は、どれでも実現されます。next_u.方法(pythone 2はnext)の対象はすべてローズマリーといいます。
リストとの違いは、リストがすべての要素を一括してメモリに読み込むのではなく、遅延計算で要素に戻すことが利点です。たとえばリストには1千万個の整数が含まれています。400 Mを超えるメモリが必要です。これはすべての要素をメモリにロードしていないので、nextメソッドを呼び出した時に戻ります。
フィボナッチの数列を例にとって、1つのローズマリーを実現します。

class Fib:
def __init__(self, n):
self.prev = 0
self.cur = 1
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.n > 0:
value = self.cur
self.cur = self.cur + self.prev
self.prev = value
self.n -= 1
return value
else:
raise StopIteration()
#   python2
def __next__(self):
return self.next()
f = Fib(10)
print([i for i in f])
#[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
ジェネレータとは
サブジェネレータを知ったら、正式にジェネレータの話題に入ることができます。普通の関数はreturnで値を返します。Javaなどの他の言語と同じですが、Pythonにはもう一つの関数があります。キーワードyieldで値を返します。この関数はジェネレータ関数といいます。関数が呼び出されるとジェネレータオブジェクトに戻ります。したがって、ローズマリーと同じ特性を持っています。唯一の違いは実現方式が違っています。後者はもっと簡潔です。
最も簡単なジェネレータ関数:

>>> def func(n):
... yield n*2
...
>>> func
<function func at 0x00000000029F6EB8>
>>> g = func(5)
>>> g
<generator object func at 0x0000000002908630>
>>>
funcはジェネレータ関数で、この関数を呼び出したときにオブジェクトを返すとジェネレータgです。このジェネレータオブジェクトの挙動は、ディケンサと非常に似ています。forループなどのシーンで使用できます。注意yieldの対応する値は、関数が呼び出された時にすぐには戻りません。nextメソッドを呼び出した時(本質的にはforループもnextメソッドを呼び出します)に戻ります。

>>> g = func(5)
>>> next(g)
10
>>> g = func(5)
>>> for i in g:
... print(i)
...
10
なぜジェネレータを使うのですか?明らかに、強制的に生成器を使用すると、ローズマリーよりもいくつかのレベルが高くなります。コードの長さがそんなに多くなく、性能も同じです。なぜ使わないですか?ジェネレーターでフィボナッチの数列を実現するのはどれぐらい簡単ですか?

def fib(n):
prev, curr = 0, 1
while n > 0:
n -= 1
yield curr
prev, curr = curr, curr + prev
print([i for i in fib(10)])
#[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
ジェネレータ式
前号の「このようにコードを書くとより優雅になる」という記事ではリスト導出式を紹介しましたが、ジェネレータ表現はリスト導出式の長さと非常に似ていますが、2人が返す対象は違っています。前者はジェネレータオブジェクトに戻り、後者はテーブルオブジェクトに戻ります。

>>> g = (x*2 for x in range(10))
>>> type(g)
<type 'generator'>
>>> l = [x*2 for x in range(10)]
>>> type(l)
<type 'list'>
以前にもジェネレータの利点を紹介しましたが、反復ハイドデータの場合、ジェネレータがより適切であることは明らかです。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。