yieldを深く理解する

4039 ワード

Python中国語開発者コミュニティから
yieldの英语の単语の意味は生产で、Pythonに触れたばかりの时はとても困惑して、ずっとyieldの使い方が分かりませんでした.ただ、yieldは、次の例のような関数に値プラグデータを返すために使用できることをざっと知っています.
def addlist(alist):
    for i in alist:
        yield i + 1

alistの各項目を取り出し、i+1を詰めます.各アイテムを呼び出して取り出します.
alist = [1, 2, 3, 4]
for x in addlist(alist):
    print(x)

これは確かにyieldアプリケーションの一例です.
1.yieldを含む関数
ある関数にyieldが含まれているのを見たら、この関数はすでにGeneratorであり、その実行は他の一般的な関数とは多くの違いがあることを意味します.たとえば、次の簡単な関数です.
def h():
    print 'To be brave'
    yield 5
h()

h()を呼び出した後、print文は実行されなかったことがわかります.これがyieldです.では、print文を実行するにはどうすればいいのでしょうか.これが後で議論する問題で、後の議論と学習を通じてyieldの動作原理がわかります.
2.yieldは式です
Python2.5以前は、yieldは文でしたが、2.5では、yieldは式(Expression)です.たとえば、次のようになります.
m = yield 5

式(yield 5)の戻り値はmに与えられるので,m=5は誤りであると考えられる.では、(yield 5)の戻り値をどのように取得しますか?後述するsend(msg)メソッドを使用する必要があります.
3.next()文を通して原理を見る
では、yieldの動作原理を説明します.yield式があるため、上のh()が呼び出されても実行されないことを知っています.そのため、next()文で実行させます.next()文は、次のyield式までGenerator実行を復元します.例:
def h():
    print 'Wen Chuan'
    yield 5
    print 'Fighting!'
c = h()
c.next()

c.next()呼び出し後、yield 5に遭遇するまでh()が実行を開始するので、結果を出力する.
Wen Chuan
c.next()を再び呼び出すと、次のyield式が見つかるまで実行が続行されます.後ろにyieldがないため、異常が放出されます.
Wen Chuan
Fighting!
Traceback (most recent call last):
  File "/home/evergreen/Codes/yidld.py", line 11, in 
    c.next()
StopIteration

4.send(msg)とnext()
next()がyieldを含む関数をどのように実行するかを理解した後,もう一つの非常に重要な関数send(msg)を見てみよう.実はnext()とsend()は一定の意味で類似しており、send()はyield式の値を伝達することができ、next()は特定の値を伝達することができず、Noneを伝達するしかないという違いがある.したがって,c.next()とc.send(None)の役割は同じであると見なすことができる.この例を見てみましょう.
def h():
    print 'Wen Chuan',
    m = yield 5  # Fighting!
    print m
    d = yield 12
    print 'We are together!'
c = h()
c.next()  #   c.send(None)
c.send('Fighting!')  #(yield 5)       'Fighting!'

出力された結果は次のとおりです.
Wen Chuan Fighting!

最初の呼び出しではnext()文またはsend(None)を使用してください.sendを使用してNone以外の値を送信することはできません.そうしないと、yield文がこの値を受信しないため、エラーが発生します.
5.send(msg)とnext()の戻り値
send(msg)とnext()には戻り値があり、それらの戻り値は特殊で、次のyield式のパラメータを返します.例えばyield 5は、5を返します.ここまで来て、何か分かったのではないでしょうか.本明細書の最初の例では、for i in alistによってGeneratorを巡回するが、実際には毎回alist.Next()が呼び出され、alist.Next()の戻り値はyieldのパラメータであり、すなわち、押し込まれたものと考えられるようになった.上記の例を続けます.
def h():
    print 'Wen Chuan',
    m = yield 5  # Fighting!
    print m
    d = yield 12
    print 'We are together!'
c = h()
m = c.next()  #m    yield 5      5
d = c.send('Fighting!')  #d    yield 12     12
print 'We will never forget the date', m, '.', d

出力結果:
Wen Chuan Fighting!
We will never forget the date 5 . 12

6.throw()とclose()割り込みGenerator
Generatorを中断するのは非常に柔軟なテクニックで、throwがGeneratorExit異常を投げ出すことでGeneratorを終了することができます.Close()メソッドの役割は同じですが、内部ではthrow(GeneratorExit)が呼び出されています.見てみましょう
def close(self):
    try:
        self.throw(GeneratorExit)
    except (GeneratorExit, StopIteration):
        pass
    else:
        raise RuntimeError("generator ignored GeneratorExit")
# Other exceptions are not caught

したがって、close()メソッドを呼び出した後、next()またはsend(msg)を呼び出すと、例外が放出されます.
Traceback (most recent call last):
  File "/home/evergreen/Codes/yidld.py", line 14, in 
    d = c.send('Fighting!')  #d    yield 12     12
StopIteration

7.Demo
def gen():
    print('111')
    m = yield 'ret 1'
    print(m)
    print('222')
    n = yield 'ret 2'
    print(n)

g = gen()
print(g.next())
print('-----------')
print(g.send('111over'))

出力結果:
111
ret 1
-----------
111over
222
ret 2