python学習ノート--理解ジェネレータ
pythonを勉強しているうちにジェネレータ(generator)という概念に触れ始めたばかりの頃は、実はあまり理解できず、感覚が完全に把握されていなかったのですが、今日この文章を見たとき、この概念について本当にもっと理解したような気がしました.感覚生成器とリスト解析の関係はrangeとxrange関数の関係に少し似ているようです.リスト解析は、処理するシーケンスをすべて作成し、ジェネレータは複数回反復してシーケンス全体を生成します.そうしないと、実行するたびに1つだけ生成されます.また、関数にはyieldキーワードが表示されます.この関数がジェネレータ関数です.
この文章はPythonのジェネレータを深く理解し、廖雪峰先生のチュートリアルも見ることができます.
ジェネレータコンセプトジェネレータは、結果を一連に保存するのではなく、ジェネレータの状態を保存し、StopIteration異常が発生するまで反復するたびに値を返します.
ジェネレータ構文1.ジェネレータ式:通リスト解析構文は,リスト解析の[]を()ジェネレータ式に置き換えることでリスト解析がほぼ可能であるが,処理が必要なシーケンスが大きい場合にはリスト解析が比較的メモリを消費する.ジェネレータ関数:関数にyieldキーワードが表示されると、その関数は通常の関数ではなく、ジェネレータ関数になります.しかし、ジェネレータ関数は無線のシーケンスを生成することができ、リストは処理できません.yieldの役割は、1つの関数をgeneratorにすることであり、yieldを持つ関数は通常の関数ではなく、Python解釈器はgeneratorと見なします.
次に、奇数を無限に生産できるジェネレータ関数を示します.
もちろん反復器を手動で書くことで同様の効果が得られるが,生成器はより直感的で分かりやすい
余談:ジェネレータにはiter()メソッドとnext()メソッドが含まれているので、StopIterationを含むカスタムIterを含まずにforを直接使用して反復することができます.手動ループでのみ反復できます.
......上記の結果を見て、今はIteratorのやり方で循環できる自信があるでしょう!
forループが実行されると、ループのたびにfab関数内部のコードが実行され、yield bに実行されるとfab関数は反復値を返し、次回の反復ではyield bの次の文からコードが実行され続け、関数のローカル変数は前回の実行中断前と全く同じように見え、関数は再びyieldに遭遇するまで実行され続けます.関数が正常に実行されている間にyieldによって数回中断されたように見え、中断するたびにyieldを通じて現在の反復値が返されます.
yieldとreturnはジェネレータにあり、returnがない場合は、デフォルトで関数が完了するまでStopIterationに戻ります.
returnが発生した場合、実行中にreturnが発生した場合、StopIterationを直接投げ出して反復を終了します.
return後に値を返す場合、この値はStopIteration異常の説明であり、プログラムの戻り値ではありません.ジェネレータはreturnを使用して値を返すことができません.
ジェネレータがサポートする方法
close()はジェネレータ関数を手動で閉じ、後の呼び出しはStopIteration異常に直接戻ります.
send()ジェネレータ関数の最大の特徴は,外部から伝達された変数を受け入れ,変数内容に基づいて結果を計算して返すことである.これはジェネレータ関数が最も理解しにくい場所であり、最も重要な場所でもあり、後でお話しする協程を実現するのはすべてそれに頼っています.
プロセスの実行:は、g.send(None)またはnext(g)によってジェネレータ関数を起動し、最初のyield文の終了位置まで実行することができる.このときyield文は実行されましたが、receiveには値が割り当てられていません.yield valueは初期値0を出力します.注意:ジェネレータ関数を起動するときはsend(None)しか使用できません.他の値を入力しようとするとエラーメッセージが表示されます. g.send(‘aaa’)を介してaaaが入力され、receiveに値が割り当てられ、valueの値が計算され、whileヘッダに戻り、yield value文の実行が停止します.このときyield valueは「got:aaa」を出力し、掛けます. g.send(3)を通過すると、2ステップ目が繰り返され、最終出力結果は「got:3」 となる. g.send(‘e’)では、プログラムはbreakを実行してループを押し出し、最後に関数全体が実行されるので、StopIteration異常が得られます.最後の実行結果は、 です.
throw()は、システム定義の例外、またはカスタムの例外を終了するジェネレータ関数に例外を送り込むために使用されます.throw()後に異常を直接飛び出してプログラムを終了したり、yieldを1つ消費したり、次のyieldがないときにプログラムの最後に直接行ったりします.
出力結果:
説明: print(next(g):normal valueが出力され、yield「normal value 2」の前にとどまります. g.throw(ValueError)が実行されているため、後続のtry文はすべてスキップされます.つまりyield‘normal value 2’は実行されず、except文に入りwe got ValueErrorhereが印刷されます.次にwhile文セクションに再び進み、yieldを消費するのでnormal valueが出力されます. print(next(g))は、yield‘normal value 2’文を実行し、その文が実行された後の位置にとどまる. g.throw(TypeError):try文が飛び出し、print(‘here’)が実行されないようになり、break文が実行され、whileループが飛び出し、プログラムの最後に達するため、StopIteration異常が飛び出します.以下に、多次元リストを展開するための総合例、すなわち、多次元リストを扁平化するための を示す.
理解が難しい場合はprint文のコメントを開いて見ておくとわかります.
まとめアヒルモデル理論によれば、ジェネレータはforを用いて反復することができる反復器である. 初めてnext(generator)を実行するとyield文が実行された後にプログラムが保留され、すべてのパラメータとステータスが保存されます.もう一度next(generator)を実行すると、保留中の状態から後に実行されます.プログラムの最後に遭遇した場合、またはStopIterationに遭遇した場合、ループは終了します. はgeneratorを通過することができる.send(arg)はパラメータを伝達し,これはコヒーレントモデルである. はgeneratorを通過することができる.throw(exception)は例外を送信します.throw文はyieldを消費します.generatorでいいです.close()を使用して、ジェネレータを手動で閉じます. next()はsend(None) に等価である.
この文章はPythonのジェネレータを深く理解し、廖雪峰先生のチュートリアルも見ることができます.
ジェネレータコンセプトジェネレータは、結果を一連に保存するのではなく、ジェネレータの状態を保存し、StopIteration異常が発生するまで反復するたびに値を返します.
ジェネレータ構文1.ジェネレータ式:通リスト解析構文は,リスト解析の[]を()ジェネレータ式に置き換えることでリスト解析がほぼ可能であるが,処理が必要なシーケンスが大きい場合にはリスト解析が比較的メモリを消費する.
>>> gen = (x**2 for x in range(5))
>>> gen
<generator object <genexpr> 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-
次に、奇数を無限に生産できるジェネレータ関数を示します.
def odd():
n=1
while True:
yield n
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
def __next__(self):
self.start +=2
return self.start
I = Iter()
for count in range(5):
print(next(I))
余談:ジェネレータにはiter()メソッドとnext()メソッドが含まれているので、StopIterationを含むカスタムIterを含まずにforを直接使用して反復することができます.手動ループでのみ反復できます.
>>> from collections import Iterable
>>> from collections import Iterator
>>> isinstance(odd_num, Iterable)
True
>>> isinstance(odd_num, Iterator)
True
>>> iter(odd_num) is odd_num
True
>>> help(odd_num)
Help on generator object:
odd = class generator(object)
| Methods defined here:
|
| __iter__(self, /)
| Implement iter(self).
|
| __next__(self, /)
| Implement 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) # next(g) , yield , 。
1
>>> next(g) # yield , , StopIteration 。
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
returnが発生した場合、実行中にreturnが発生した場合、StopIterationを直接投げ出して反復を終了します.
>>> def g2():
... yield 'a'
... return
... yield 'b'
...
>>> g=g2()
>>> next(g) # yield 'a' 。
'a'
>>> next(g) # return, StopIteration , yield 'b' 。
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
return後に値を返す場合、この値はStopIteration異常の説明であり、プログラムの戻り値ではありません.ジェネレータはreturnを使用して値を返すことができません.
>>> def g3():
... yield 'hello'
... return 'world'
...
>>> g=g3()
>>> next(g)
'hello'
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration: world
ジェネレータがサポートする方法
>>> help(odd_num)
Help on generator object:
odd = class generator(object)
| Methods defined here:
......
| close(...)
| close() -> raise GeneratorExit inside generator.
|
| send(...)
| send(arg) -> send 'arg' into generator,
| return next yielded value or raise StopIteration.
|
| throw(...)
| throw(typ[,val[,tb]]) -> raise exception in generator,
| return next yielded value or raise StopIteration.
close()はジェネレータ関数を手動で閉じ、後の呼び出しはStopIteration異常に直接戻ります.
>>> def g4():
... yield 1
... yield 2
... yield 3
...
>>> g=g4()
>>> next(g)
1
>>> g.close()
>>> next(g) # ,yield 2 yield 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
send()ジェネレータ関数の最大の特徴は,外部から伝達された変数を受け入れ,変数内容に基づいて結果を計算して返すことである.これはジェネレータ関数が最も理解しにくい場所であり、最も重要な場所でもあり、後でお話しする協程を実現するのはすべてそれに頼っています.
def gen():
value=0
while True:
receive=yield value
if receive=='e':
break
value = 'got: %s' % receive
g=gen()
print(g.send(None))
print(g.send('aaa'))
print(g.send(3))
print(g.send('e'))
プロセスの実行:
0
got: aaa
got: 3
Traceback (most recent call last):
File "h.py", line 14, in <module>
print(g.send('e'))
StopIteration
throw()は、システム定義の例外、またはカスタムの例外を終了するジェネレータ関数に例外を送り込むために使用されます.throw()後に異常を直接飛び出してプログラムを終了したり、yieldを1つ消費したり、次のyieldがないときにプログラムの最後に直接行ったりします.
def gen():
while True:
try:
yield 'normal value'
yield 'normal value 2'
print('here')
except ValueError:
print('we got ValueError here')
except TypeError:
break
g=gen()
print(next(g))
print(g.throw(ValueError))
print(next(g))
print(g.throw(TypeError))
出力結果:
normal value
we got ValueError here
normal value
normal value 2
Traceback (most recent call last):
File "h.py", line 15, in <module>
print(g.throw(TypeError))
StopIteration
説明:
def flatten(nested):
try:
# , TypeError。
if isinstance(nested, str):
raise TypeError
for sublist in nested:
#yield flatten(sublist)
for element in flatten(sublist):
#yield element
print('got:', element)
except TypeError:
#print('here')
yield nested
L=['aaadf',[1,2,3],2,4,[5,[6,[8,[9]],'ddf'],7]]
for num in flatten(L):
print(num)
理解が難しい場合はprint文のコメントを開いて見ておくとわかります.
まとめ