深入理解 Python yield
yield的英文單詞意思是生產,剛接觸Python的時候感到非常困惑,一直沒弄明白yield的用法。一直到稀里糊涂的看完了廖雪峰的python博客也沒徹底明白。
有一次不小心看到了這個文章,是轉載的,原文出自哪里我也不知道,08年的文章,python2.5的,于是我按照3.6的標準重新定義一下,最后會附上源碼
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 5print('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 5print('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 """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) StopIterationsend(msg) 與 next()
了解了next()如何讓包含yield的函數執行后,我們再來看另外一個非常重要的函數send(msg)。
其實next()和send()在一定意義上作用是相似的
區別
send()可以傳遞yield的值
next()只能傳遞None。
所以next() 和 send(None)作用是一樣的。
def s():print('study yield')m = yield 5print(m)d = yield 16print('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 5print(m)d = yield 16print('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.'''# 用于在生成器中拋出一個異常。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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GNU的C++代码书写规范,C语言之父D
- 下一篇: 兔子生兔子循环python_python