深刻理解 Python yield

https://blog.csdn.net/lftaoyuan/article/details/78915518python

 

python2和python3是不兼容的,通篇環境都是python3.6git

簡單的yield實例

之前只是粗略的知道yield能夠用來爲一個函數返回值塞數據,好比下面的例子:github

def addlist(alist): for i in alist: yield i + 1

 

取出alist的每一項,而後把i + 1塞進去。而後經過調用取出每一項:ruby

alist = [1, 2, 3, 4] for x in addlist(alist): print(x)

這的確是yield應用的一個例子,可是,看過不少東西,並本身反覆體驗後,對yield有了一個全新的理解,其中這篇算是精品了。ide

包含yield的函數

假如你看到某個函數包含了yield,這意味着這個函數已是一個Generator,它的執行會和其餘普通的函數有不少不一樣。好比下面的簡單的函數:函數

def h(): print('study yield') yield 5 print('go on!') h()

 

能夠看到,調用h()以後,print 語句並無執行!這就是yield。具體的內容後面會愈來愈清晰,包括yield的工做原理。lua

yield是一個表達式

python 2.5之前,yield是一個語句,我也沒有考證,由於早都不用了,如今yield是一個表達式:idea

m = yield 5

表達式(yield 5)的返回值將賦值給m,因此,m = 5 確定是錯的。spa

那麼如何獲取(yield 5)的返回值呢?須要用到send(msg).net

yield工做原理

揭曉yield的工做原理,須要配合next()函數。上面的h()被調用後並無執行,由於它有yield表達式,經過next()能夠恢復Generator執行,直到下一個yield

def h(): print('study yield') yield 5 print('go on!') c = h() d1 = next(c) # study yield d2 = next(c) """ study yield go on! Traceback (most recent call last): File "D:/idea/workspace/pythonSpace/PythonDemo/static/yield_demo.py", line 35, in <module> d2 = next(c) StopIteration """
  • 1
  • 2
  • 3
  • 4

next()被調用後,h()開始執行,直到遇到yield 5

所以輸出結果是:study yield

當咱們再次調用next()時,會繼續執行,直到找到下一個yield。因爲後面沒有yield了,所以會拋出異常:

study yield go on! Traceback (most recent call last): File "D:/idea/workspace/pythonSpace/PythonDemo/static/yield_demo.py", line 35, in <module> d2 = next(c) StopIteration

 

send(msg) 與 next()

瞭解了next()如何讓包含yield的函數執行後,咱們再來看另一個很是重要的函數send(msg)

其實next()send()在必定意義上做用是類似的

區別

send()能夠傳遞yield的值

next()只能傳遞None

因此next() 和 send(None)做用是同樣的。

def s(): print('study yield') m = yield 5 print(m) d = yield 16 print('go on!') c = s() s_d = next(c) # 至關於send(None) c.send('Fighting!') # (yield 5)表達式被賦予了'Fighting!'

 

輸出的結果爲:

study yield Fighting!

 

注意 生成器剛啓動時(第一次調用),請使用next()語句或是send(None),不能直接發送一個非None的值,不然會報TypeError,由於沒有yield語句來接收這個值。

send(msg) 與 next()的返回值

send(msg) 和 next() 的返回值比較特殊,是下一個yield表達式的參數(yield 5,則返回 5)。

到這裏,第一個例子中,經過for i in alist 遍歷 Generator,實際上是每次都調用了next(),而每次next()的返回值正是yield的參數:

def s(): print('study yield') m = yield 5 print(m) d = yield 16 print('go on!') c = s() s_d1 = next(c) # 至關於send(None) s_d2 = c.send('Fighting!') # (yield 5)表達式被賦予了'Fighting!' print('My Birth Day:', s_d1, '.', s_d2)

 

輸出結果:

study yield Fighting! My Birth Day: 5 . 16

 

中斷Generator

上面的例子中,當沒有可執行程序的時候,會拋出一個StopIteration, 開發過程當中,中斷Generator是一個很是靈活的技巧

throw

經過拋出一個GeneratorExit異常來終止Generator。

close

close的做用和throw是同樣的,看它的源碼,能夠發現,它和raise一球樣

def throw(self, type, value=None, traceback=None): '''Used to raise an exception inside the generator.''' # 用於在生成器中拋出一個異常。 pass def close(self): '''Raises new GeneratorExit exception inside the generator to terminate the iteration.''' # 在生成器中生成新的GeneratorExit異常來終止迭代。 pass

 

其實最後一箇中斷生成器能夠忽略的,在開發過程當中,不可避免的要用到這些,可是Python3內部已經作得很好了,通常不太須要手動去作這件事情。

demo地址

https://github.com/seeways/PythonDemo/blob/master/static/yield_demo.py

相關文章
相關標籤/搜索