(六)ジェネレータ
1.ジェネレータとは
リスト生成式により、リストを直接作成できます.ただし、メモリの制限を受けると、リストの容量は限られているに違いありません.また、100万個の要素を含むリストを作成すると、大きなストレージスペースを消費するだけでなく、前のいくつかの要素にアクセスするだけで、後ろのほとんどの要素が消費するスペースが無駄になります.だから、リスト要素が何らかのアルゴリズムで推定できるなら、ループの過程で後続の要素を絶えず推定することができますか?これにより、listを完全に作成する必要がなくなり、大量のスペースを節約できます.Pythonでは,このように循環しながら計算するメカニズムをジェネレータ:generatorと呼ぶ.
2、ジェネレータの作成方法(一)
ジェネレータを作成するには、さまざまな方法があります.1つ目の方法は,リスト生成式の[]を()に変更するだけで簡単である.
LとGを作成する違いは、最外層の[]と()のみであり、Lはリストであり、Gはジェネレータである.Lの各要素を直接印刷することができますが、Gの各要素をどのように印刷しますか?1つずつ印刷する場合は、next()関数を使用してジェネレータの次の戻り値を取得します.
ジェネレータは、next(G)を呼び出すたびにGの次の要素の値を計算し、最後の要素まで計算し、より多くの要素がない場合にStopIterationの異常を放出するアルゴリズムを保存します.もちろん、next()を呼び出し続ける方法は望ましくなく、ジェネレータも反復可能なオブジェクトであるため、forループを使用するのが正しい.したがって、ジェネレータを作成すると、next()はほとんど呼び出されず、forループで反復され、StopIteration異常に関心を持つ必要はありません.
3.ジェネレータの作成方法(二)
generatorはとても強いです.推定アルゴリズムが複雑で,リスト生成式のようなforループでは実現できない場合,関数で実現することも可能である.例えば、有名なフィボナッチ数列(Fibonacci)は、1番目と2番目の数を除いて、いずれの数も前の2つの数から加算することができます:1、1、2、3、5、8、13、21、34、...
フィポラチ数列はリスト生成式では書けませんが、関数で印刷するのは簡単です.
fib関数は実際にフィボラッチ数列を定義した推定規則であり,最初の要素から後続の任意の要素を推定することができ,この論理はgeneratorに非常に類似していることがわかる.print(b)をyield bに変更すればいいのです
4. send
yieldに実行するとgen関数の役割が一時的に保存され、iの値が返されます.tempは次のc.send(「python」)すなわちsendから送られてきた値を受信し,c.next()はc.send(None)に等価である.
next関数を使用してアクセス
使用__next__()メソッドアクセス
send法によるアクセス
まとめ
ジェネレータは、前回戻ったときの関数体の位置を記憶する関数です.ジェネレータ関数の2回目(またはn回目)の呼び出しは、その関数の間にジャンプし、最後に呼び出されたすべてのローカル変数は変更されません.
ジェネレータはデータの状態を「覚えている」だけでなく、データの状態を「覚えている」だけでなく、ジェネレータはまた、ストリーム制御構造(コマンドプログラミングでは、データ値だけではない)における位置を「記憶」します.
ジェネレータの特徴:1.メモリの節約2.次の呼び出しに反復すると、使用するパラメータは最初に保持されます.つまり、すべての関数呼び出しのパラメータが最初に呼び出されたときに保持され、新しく作成されたものではなく、すべての関数呼び出しのパラメータが最初に呼び出されたときに保持されます.
リスト生成式により、リストを直接作成できます.ただし、メモリの制限を受けると、リストの容量は限られているに違いありません.また、100万個の要素を含むリストを作成すると、大きなストレージスペースを消費するだけでなく、前のいくつかの要素にアクセスするだけで、後ろのほとんどの要素が消費するスペースが無駄になります.だから、リスト要素が何らかのアルゴリズムで推定できるなら、ループの過程で後続の要素を絶えず推定することができますか?これにより、listを完全に作成する必要がなくなり、大量のスペースを節約できます.Pythonでは,このように循環しながら計算するメカニズムをジェネレータ:generatorと呼ぶ.
2、ジェネレータの作成方法(一)
ジェネレータを作成するには、さまざまな方法があります.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]: at 0x7f626c132db0>
In [19]:
LとGを作成する違いは、最外層の[]と()のみであり、Lはリストであり、Gはジェネレータである.Lの各要素を直接印刷することができますが、Gの各要素をどのように印刷しますか?1つずつ印刷する場合は、next()関数を使用してジェネレータの次の戻り値を取得します.
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)
24-380e167d6934> in ()
----> 1 next(G)
StopIteration:
In [25]:
In [26]: G = ( x*2 for x in range(5))
In [27]: for x in G:
....: print(x)
....:
0
2
4
6
8
In [28]:
ジェネレータは、next(G)を呼び出すたびにGの次の要素の値を計算し、最後の要素まで計算し、より多くの要素がない場合にStopIterationの異常を放出するアルゴリズムを保存します.もちろん、next()を呼び出し続ける方法は望ましくなく、ジェネレータも反復可能なオブジェクトであるため、forループを使用するのが正しい.したがって、ジェネレータを作成すると、next()はほとんど呼び出されず、forループで反復され、StopIteration異常に関心を持つ必要はありません.
3.ジェネレータの作成方法(二)
generatorはとても強いです.推定アルゴリズムが複雑で,リスト生成式のようなforループでは実現できない場合,関数で実現することも可能である.例えば、有名なフィボナッチ数列(Fibonacci)は、1番目と2番目の数を除いて、いずれの数も前の2つの数から加算することができます:1、1、2、3、5、8、13、21、34、...
フィポラチ数列はリスト生成式では書けませんが、関数で印刷するのは簡単です.
In [28]: def fib(times):
....: n = 0
....: a,b = 0,1
....: while n1
....: return 'done'
....:
In [29]: fib(5)
1
1
2
3
5
Out[29]: 'done'
fib関数は実際にフィボラッチ数列を定義した推定規則であり,最初の要素から後続の任意の要素を推定することができ,この論理はgeneratorに非常に類似していることがわかる.print(b)をyield bに変更すればいいのです
In [30]: def fib(times):
....: n = 0
....: a,b = 0,1
....: while n1
....: 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)
37-8c2b02b4361a> in ()
----> 1 next(F)
StopIteration: done
4. send
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
....:
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)
17-468f0afdf1b9> in ()
----> 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)
24-39ec527346a9> in ()
----> 1 f.__next__()
StopIteration:
send法によるアクセス
In [43]: f = gen()
In [44]: f.__next__()
Out[44]: 0
In [45]: f.send('haha')
haha
Out[45]: 1
In [46]: f.__next__()
None
Out[46]: 2
In [47]: f.send('haha')
haha
Out[47]: 3
In [48]:
まとめ
ジェネレータは、前回戻ったときの関数体の位置を記憶する関数です.ジェネレータ関数の2回目(またはn回目)の呼び出しは、その関数の間にジャンプし、最後に呼び出されたすべてのローカル変数は変更されません.
ジェネレータはデータの状態を「覚えている」だけでなく、データの状態を「覚えている」だけでなく、ジェネレータはまた、ストリーム制御構造(コマンドプログラミングでは、データ値だけではない)における位置を「記憶」します.
ジェネレータの特徴:1.メモリの節約2.次の呼び出しに反復すると、使用するパラメータは最初に保持されます.つまり、すべての関数呼び出しのパラメータが最初に呼び出されたときに保持され、新しく作成されたものではなく、すべての関数呼び出しのパラメータが最初に呼び出されたときに保持されます.