Python のジェネレーターについての深い理解

黄舟
リリース: 2016-12-16 11:36:54
オリジナル
1667 人が閲覧しました

ジェネレーターの概念

ジェネレーターは、一連の結果を保存しませんが、ジェネレーターの状態を保存し、StopIteration 例外が発生するまで反復するたびに値を返します。

ジェネレーター構文

ジェネレーター式: リスト解析構文と同じですが、リスト解析の[]を()に置き換えます
ジェネレーター式でできることは基本的にリスト解析で処理できますが、処理が必要な場合はシーケンスが比較的大きい場合、リストの解析により多くのメモリが消費されます。

>>> gen = (x**2 for x in range(5))
>>> gen
at 0x0000000002FB7B40>
>>> for g in gen:
... print(g, end='-')
...
0-1-4-9-16-
>>> for x in [0,1,2 ,3,4,5]:
... print(x, end='-')
...
0-1-2-3-4-5-

ジェネレーター関数: 関数内 if If yield キーワードが出現すると、その関数は通常の関数ではなく、ジェネレーター関数になります。
しかし、ジェネレーター関数は無制限のシーケンスを生成する可能性があるため、リストはまったく処理できません。
yield の機能は、関数をジェネレーターに変えることです。yield を備えた関数は通常の関数ではなくなり、Python インタープリターはそれをジェネレーターとして扱います。

以下は奇数を無限に生成できるジェネレータ関数です。

def odd():
n=1
while True:
n+=2
odd_num = odd()
count = 0
for o in odd_num:
if count >=5: Break
print( o)
Count +=1

もちろん、反復子を手動で作成しても同様の効果を実現できますが、ジェネレーターの方が直感的で理解しやすいです

class Iter:

def __init__(self):
self.start=-1
DEF __iter __ (Self):
Return Self __next __ (Self):
Self.start += 2
Return Self.start
i = it ()
For Count in Range (5):
Print (NEXT (i) )


余談: ジェネレーターには __iter() メソッドと next__() メソッドが含まれているため、for を直接使用して反復できますが、StopIteration を含まない自己作成の Iter は手動ループでのみ反復できます。 isinstance(odd_num, Iterator)

True

>>> コレクションからの Iterator

>>> isinstance(odd_num, Iterator) )

True
>>> iter(odd_num) は、odd_num
True
>>> help(odd_num)
ジェネレーター オブジェクトのヘルプ:

odd = classgenerator(object)
| メソッドはここで定義されます:
|
| iter(self) を実装します。
|
| __next__(self, /)
| その結果、自信を持って Iterator メソッドに従ってループできるようになりました。

for ループが実行されると、各ループは fab 関数内のコードを実行し、yield b に達すると、fab 関数は反復値を返し、次の反復ではコードが yield の次のステートメントから実行されます。 b、および 関数のローカル変数は最後の中断前とまったく同じに見えるため、関数は再び yield が発生するまで実行を続けます。通常の実行中に関数が yield によって数回中断され、各中断が yield を通じて現在の反復値を返しているように見えます。

yield と return

ジェネレーターでは、return がない場合、関数が完了するとデフォルトで StopIteration が返されます

>>> def g1():

... yield 1

。 ..

>>> g=g1()

>>> next(g) を初めて呼び出すと、yield ステートメントの実行後にハングします。実行は終了しません。

1

>>> next(g) #プログラムは yield ステートメントの次のステートメントから実行を開始しようとしましたが、最後に到達したことが判明したため、StopIteration 例外がスローされます。

トレースバック (最新の呼び出しは最後):

ファイル ""、行 1、

StopIteration

>>>内


return が発生した場合、return が実行中の場合、Then StopIteration を直接スローして反復を終了します。

>>> def g2():
... 'a'
を返す...
>>> g=g2()
>>> next(g) #プログラムは yield 'a' ステートメントの実行後の位置に留まります。
'a'
>>> next(g) #プログラムは次のステートメントが return であることを検出したため、StopIteration 例外がスローされ、yield 'b' ステートメントは実行されません。
トレースバック (最新の呼び出しは最後):
内のファイル ""、行 1、
StopIteration


return 後に値が返された場合、この値は StopIteration 例外の説明です。プログラムではありません 戻り値。

ジェネレーターが return を使用して値を返す方法はありません。

>>> def g3():

... 'hello' を返す

...
>>> g=g3()
>> > next(g)
'hello'
>>> next(g)
トレースバック (最後の呼び出し):
ファイル ""、 の行 1: world


ジェネレータでサポートされているメソッド

>>> ジェネレータオブジェクトのヘルプ:

odd = classgenerator(object)

| ここで定義されているメソッド:

......
| close(...)
-> ジェネレーター内で終了します
|
send(arg) -> 次の結果を返します。値を取得するか、StopIteration.
を発生させます。 ....


close()

ジェネレーター関数を手動で閉じると、後続の呼び出しで直接 StopIteration 例外が返されます。

>>> def g4():
... 収量 1
... 収量 2
... 収量 3
>>> g=g4()
> >> next(g)

1

>>> g.close()

>>> next(g) #閉じた後は、yield 2 および yield 3 ステートメントは機能しなくなります

(最新の呼び出しは最後):

File "", line 1, in

StopIteration


send()

ジェネレーター関数の最大の特徴は、渡された変数を受け入れることができることです。変数の内容を元に結果を計算して返します。
これはジェネレーター関数について理解するのが最も難しいことであり、後で説明するコルーチンの実装に依存するものでもあります。

def gen():
value=0
while True:
accept=yield value
if accept=='e':
Break

value = 'got: %s' % accept

g=gen()

print(g.send(None))
print(g.send('aaa'))

print(g.send(3))

print(g.send('e'))


実行プロセス:

ジェネレーター関数は、g.send(None) または next(g) を通じて開始でき、最初の yield ステートメントの終わりまで実行できます。
この時点では、yield ステートメントは実行されていますが、receive には値が割り当てられていません。
yield value は初期値 0 を出力します
注: ジェネレーター関数を開始するときは、send(None) のみを使用できます。他の値を入力しようとすると、エラー メッセージが表示されます。


g.send('aaa') を通じて aaa が渡され、receive に代入され、value の値が計算されて while ヘッドに返され、yield value ステートメントの実行が停止されます。
このとき、yield値は「got: aaa」と出力されてハングします。

g.send(3) を通じてステップ 2 が繰り返され、最終的な出力結果は "got: 3" になります


g.send('e') を実行すると、プログラムは Break を実行して終了しますループ、そして最後に関数全体が実行されるため、StopIteration 例外が発生します。
最終的な実行結果は次のとおりです:

0

got: aaa

got: 3
Traceback (most last call last):
ファイル "h.py"、14 行目、

print(g. send( 'e'))

StopIteration

throw()

は、システム定義の例外またはカスタム例外を終了できるジェネレーター関数に例外を送信するために使用されます。

Throw() は例外を直接スローしてプログラムを終了するか、yield を消費するか、次の yield がない場合はプログラムの最後に直接進みます。
)Def Gen ():

WHILE TRUE:
Try:
yield '通常値'
yield' 通常値 2'
print ('hee')
Valueerror を除く:
Print ('We Got Valueerror Heres ') X Except Typeerror ; 結果は次のようになります:

正常値
ここで ValueError が発生しました
正常値
正常値 2
トレースバック (最新の呼び出しは最後):
ファイル "h.py"、15 行目、
print(g .throw (TypeError))
StopIteration

説明:

print(next(g)): 正常値を出力し、「正常値 2」が生成される前に留まります。


g.throw(ValueError) が実行されるため、後続のすべての try ステートメントはスキップされます。つまり、yield 'normal value 2' は実行されません。その後、Except ステートメントを入力して、ここで ValueError が得られたことを出力します。
その後、もう一度while文部分を入力してyieldを消費すると、通常の値が出力されます。


print(next(g)) は、 yield 'normal value 2' ステートメントを実行し、ステートメントの実行後の位置に留まります。

g.throw(TypeError): try ステートメントから飛び出すため、print('here') は実行されません。その後、break ステートメントを実行し、while ループから飛び出て、最後に到達します。そのため、StopIteration 例外がスローされます。

以下は、多次元リストを展開する、または多次元リストをフラット化するための包括的な例です)

def flatten(nested):


try:
#文字列の場合は、手動で TypeError をスローします。 If isInstance (Nested, Str):

ネストされたサブリストの場合は Typerror

を発生させる:
#YIELD FLATTEN (SUBLIST)

Flatten の要素の場合:

#Yield Element

Print ('GOT : ', Element)

TypeError を除く:

#print('here')

yield ネスト

L=['aaadf',[1,2,3],2,4,[5,[6,[8,[9]],'ddf '], 7]]
for num in flatten(L):
print(num)


ちょっとわかりにくい場合は、print 文のコメントを開いて確認するとわかりやすいです。

yield from

yield で生成される関数は反復子なので、通常はループ文の中に入れて結果を出力します。
場合によっては、この yield によって生成されたイテレーターを別のジェネレーター関数、つまりジェネレーターのネストに入れる必要があります。
たとえば、次の例:

def inner():
for i in range(10):
yield i
def inner():

g_inner=inner() #これはジェネレータです

while True:

res = g_inner.send(None)

yield res

g_outer=outer()
while True:
try:

print(g_outer.send(None))

この時点で、 yield from ステートメントを使用して、仕事量を減らす。

def inner2():
Yield from inner()


もちろん、yield from ステートメントの焦点は、内部層と外部層の間の例外を自動的に処理できるようにすることです。ここには 2 つのよく書かれた記事があります。もう小言は言わないよ。
http://blog.theerrorlog.com/yield-from-in-python-3.html
http://stackoverflow.com/questions/9708902/in-practice-what-are-the-main-uses-for -the-new-yield-from-syntax-in-python-3

概要

アヒルモデル理論によれば、ジェネレーターは一種の反復子であり、for を使用して反復できます。


初めて next(generator) を実行すると、yield ステートメントの実行後にプログラムが一時停止され、すべてのパラメータとステータスが保存されます。

再度next(generator)を実行するとサスペンド状態から実行されます。

ループは、プログラムの終わりに遭遇するか、StopIteration に遭遇すると終了します。


コルーチン モデルであるgenerator.send(arg)を通じてパラメータを渡すことができます。

generator.throw(Exception) を通じて例外を渡すことができます。 throw ステートメントは yield を消費します。
generator.close() を使用してジェネレーターを手動で閉じることができます。

next() は send(None) と同等です

上記は Python のジェネレーターを深く理解するための内容です。その他の関連記事については、PHP 中国語 Web サイト (www.php.cn) を参照してください。 !


関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート