Pythonでのジェネレータの詳細
前言:前のブログでは、ジェネレータ:Python反復器とジェネレータのまとめ【推奨コレクション】を紹介しましたが、このブログではPythonのジェネレータについてシステム紹介します.
文書ディレクトリ 一、ジェネレータ概要 二、使用()作成ジェネレータ 三、yieldを使用してジェネレータ を作成する四、sendを使用して を起動する
一、ジェネレータの概要
反復器を用いて,反復してデータを取得するたびに(
二、()を使用してジェネレータを作成する
ジェネレータを作成するには、さまざまな方法があります.1つ目の方法は簡単で,1つのリスト生成式の[]を()に変更すると,以下のようになる.
LとGを作成する違いは、最外層の[]と()のみであり、Lはリストであり、Gはジェネレータである.リストLの各要素を直接印刷することができ、ジェネレータGでは、
三、yieldを使ってジェネレータを作成する
generatorはとても強いです.推定アルゴリズムが複雑であれば,類似リスト生成式の
前節で述べたフィボナッチ数列を例に挙げて、前節で反復器を用いた実装方法を思い出します.
注意:反復器で実装する方法では、反復の状態をいくつかの変数(n、current、num 1、num 2)で保存します.ジェネレータで実現しましょう.
ジェネレータ実装を使用する方法では、反復器
このとき、関数を呼び出すように(ケースではF=fib(5))ジェネレータを使用すると、実行関数体ではなく、ジェネレータオブジェクト(ケースではF)が返され、反復器を使用するようにジェネレータを使用できます.
しかし,generatorを
まとめ: 現在の運転状態(ブレークポイント)を保存し、ジェネレータ(関数)が一時停止します. は、
は、 Python 3のジェネレータは、
四、sendで起動する
例:
sendの使用
next関数の使用
使用__next__()メソッド(あまり使用されません)
文書ディレクトリ
一、ジェネレータの概要
反復器を用いて,反復してデータを取得するたびに(
next()
法により)特定の法則に従って生成することができる.しかし、反復器を実装する場合、現在反復されている状態については、現在の状態に基づいて次のデータを生成するために、自分で記録する必要があります.現在の状態を記録し、next()
関数と組み合わせて反復的に使用するために、より簡便な構文、すなわちジェネレータを採用することができる.ジェネレータは特殊な反復器です.二、()を使用してジェネレータを作成する
ジェネレータを作成するには、さまざまな方法があります.1つ目の方法は簡単で,1つのリスト生成式の[]を()に変更すると,以下のようになる.
In [15]: L = [ x*2 for x in range(5)]
In [16]: L
Out[16]: [0, 2, 4, 6, 8]
In [17]: G = ( x*2 for x in range(5))
In [18]: G
Out[18]: <generator object <genexpr> at 0x7f626c132db0>
LとGを作成する違いは、最外層の[]と()のみであり、Lはリストであり、Gはジェネレータである.リストLの各要素を直接印刷することができ、ジェネレータGでは、
next()
関数、for
ループ、list()などの方法で反復器の使用方法で使用することができる.In [19]: next(G)
Out[19]: 0
In [20]: next(G)
Out[20]: 2
In [21]: next(G)
Out[21]: 4
In [22]: next(G)
Out[22]: 6
In [23]: next(G)
Out[23]: 8
In [24]: next(G)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-24-380e167d6934> in <module>()
----> 1 next(G)
StopIteration:
In [25]: G = ( x*2 for x in range(5))
In [26]: for x in G:
....: print(x)
....:
0
2
4
6
8
三、yieldを使ってジェネレータを作成する
generatorはとても強いです.推定アルゴリズムが複雑であれば,類似リスト生成式の
for
サイクルでは実現できない場合,関数で実現することも可能である.前節で述べたフィボナッチ数列を例に挙げて、前節で反復器を用いた実装方法を思い出します.
class FibIterator(object):
""" """
def __init__(self, n):
"""
:param n: int, n
"""
self.n = n
# current
self.current = 0
# num1 , 0
self.num1 = 0
# num2 , 1
self.num2 = 1
def __next__(self):
""" next() """
if self.current < self.n:
num = self.num1
self.num1, self.num2 = self.num2, self.num1+self.num2
self.current += 1
return num
else:
raise StopIteration
def __iter__(self):
""" __iter__ """
return self
注意:反復器で実装する方法では、反復の状態をいくつかの変数(n、current、num 1、num 2)で保存します.ジェネレータで実現しましょう.
In [30]: def fib(n):
....: current = 0
....: num1, num2 = 0, 1
....: while current < n:
....: num = num1
....: num1, num2 = num2, num1+num2
....: current += 1
....: yield num
....: return 'done'
....:
In [31]: F = fib(5)
In [32]: next(F)
Out[32]: 1
In [33]: next(F)
Out[33]: 1
In [34]: next(F)
Out[34]: 2
In [35]: next(F)
Out[35]: 3
In [36]: next(F)
Out[36]: 5
In [37]: next(F)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-37-8c2b02b4361a> in <module>()
----> 1 next(F)
StopIteration: done
ジェネレータ実装を使用する方法では、反復器
__next__
法で実装されていた基本論理を1つの関数に配置して実装したが、反復ごとに数値を返すreturn
をyield
に変換すると、新しく定義された関数は関数ではなくジェネレータになる.簡単に言えばdefにyield
のキーワードがある限りジェネレータと呼ぶこのとき、関数を呼び出すように(ケースではF=fib(5))ジェネレータを使用すると、実行関数体ではなく、ジェネレータオブジェクト(ケースではF)が返され、反復器を使用するようにジェネレータを使用できます.
In [38]: for n in fib(5):
....: print(n)
....:
1
1
2
3
5
しかし,generatorを
for
サイクルで呼び出すと,generatorのreturn
文の戻り値が得られないことが分かった.戻り値を取得するには、StopIterationエラーを取得する必要があります.戻り値はStopIterationのvalueに含まれます.In [39]: g = fib(5)
In [40]: while True:
....: try:
....: x = next(g)
....: print("value:%d"%x)
....: except StopIteration as e:
....: print(" :%s"%e.value)
....: break
....:
value:1
value:1
value:2
value:3
value:5
:done
まとめ:
yield
キーワードを使用した関数は、関数ではなくジェネレータ(yield
を使用した関数がジェネレータ)です.yield
キーワードには2つの役割があります.yield
キーワードの後の式の値を戻り値として返し、この場合、return
の役割を果たしていると理解できる.next()
関数を使用して、ジェネレータをブレークポイントから実行し続けることができ、すなわち、ジェネレータ(関数)を起動させることができる.return
を使用して最終的に実行された戻り値を返すことができますが、Python 2のジェネレータは、return
を使用して戻り値を返すことはできません(つまり、return
を使用してジェネレータから終了することができますが、return
以降は式がありません).四、sendで起動する
next()
関数を使用してジェネレータを起動して実行を継続できるほか、send()
関数を使用して実行を起動することもできます.send()
関数を使用する利点の1つは、起動と同時にブレークポイントに追加のデータを転送できることである.例:
yield
に実行するとgen関数作用が一時保存され、iの値が返される.tempは次のc.send(「python」)を受信し,sendが送信した値,c.next()はc.send(None)に等価である.In [10]: def gen():
....: i = 0
....: while i<5:
....: temp = yield i
....: print(temp)
....: i+=1
sendの使用
In [43]: f = gen()
In [44]: next(f)
Out[44]: 0
In [45]: f.send('haha')
haha
Out[45]: 1
In [46]: next(f)
None
Out[46]: 2
In [47]: f.send('haha')
haha
Out[47]: 3
next関数の使用
In [11]: f = gen()
In [12]: next(f)
Out[12]: 0
In [13]: next(f)
None
Out[13]: 1
In [14]: next(f)
None
Out[14]: 2
In [15]: next(f)
None
Out[15]: 3
In [16]: next(f)
None
Out[16]: 4
In [17]: next(f)
None
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-17-468f0afdf1b9> in <module>()
----> 1 next(f)
StopIteration:
使用__next__()メソッド(あまり使用されません)
In [18]: f = gen()
In [19]: f.__next__()
Out[19]: 0
In [20]: f.__next__()
None
Out[20]: 1
In [21]: f.__next__()
None
Out[21]: 2
In [22]: f.__next__()
None
Out[22]: 3
In [23]: f.__next__()
None
Out[23]: 4
In [24]: f.__next__()
None
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-24-39ec527346a9> in <module>()
----> 1 f.__next__()
StopIteration: