標籤:The 函數 name pass param ams 接收 python ace
78915518
python2和python3是不相容的,通篇環境都是python3.6
簡單的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
應用的一個例子,但是,看過很多東西,並自己反覆體驗後,對yield有了一個全新的理解,其中這篇算是精品了。
包含yield的函數
假如你看到某個函數包含了yield
,這意味著這個函數已經是一個Generator
,它的執行會和其他普通的函數有很多不同。比如下面的簡單的函數:
def h(): print(‘study yield‘) yield 5 print(‘go on!‘)h()
可以看到,調用h()
之後,print 語句並沒有執行!這就是yield
。具體的內容後面會越來越清晰,包括yield
的工作原理。
yield是一個運算式
python 2.5以前,yield
是一個語句,我也沒有考證,因為早都不用了,現在yield
是一個運算式:
m = yield 5
運算式(yield 5)的傳回值將賦值給m,所以,m = 5
肯定是錯的。
那麼如何擷取(yield 5)的傳回值呢?需要用到send(msg)
。
yield工作原理
揭曉yield
的工作原理,需要配合next()
函數。上面的h()
被調用後並沒有執行,因為它有yield
運算式,通過next()
可以恢複Generator
執行,直到下一個yield
。
def h(): print(‘study yield‘) yield 5 print(‘go on!‘)c = h()d1 = next(c) # study yieldd2 = next(c)"""study yieldgo on!Traceback (most recent call last): File "D:/idea/workspace/pythonSpace/PythonDemo/static/yield_demo.py", line 35, in <module> d2 = next(c)StopIteration"""
next()
被調用後,h()
開始執行,直到遇到yield 5
因此輸出結果是:study yield
當我們再次調用next()
時,會繼續執行,直到找到下一個yield
。由於後面沒有yield
了,因此會拋出異常:
study yieldgo 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 yieldFighting!
注意 產生器剛啟動時(第一次調用),請使用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 yieldFighting!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.‘‘‘ # 用於在產生器中拋出一個異常。 passdef 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
深入理解 Python yield