深入理解 Python yield
yield的英文單詞意思是生產(chǎn),剛接觸Python的時(shí)候感到非常困惑,一直沒(méi)弄明白yield的用法。一直到稀里糊涂的看完了廖雪峰的python博客也沒(méi)徹底明白。
有一次不小心看到了這個(gè)文章,是轉(zhuǎn)載的,原文出自哪里我也不知道,08年的文章,python2.5的,于是我按照3.6的標(biāo)準(zhǔn)重新定義一下,最后會(huì)附上源碼
python2和python3是不兼容的,通篇環(huán)境都是python3.6
簡(jiǎn)單的yield實(shí)例
以前只是粗略的知道yield可以用來(lái)為一個(gè)函數(shù)返回值塞數(shù)據(jù),比如下面的例子:
def addlist(alist):for i in alist:yield i + 1取出alist的每一項(xiàng),然后把i + 1塞進(jìn)去。然后通過(guò)調(diào)用取出每一項(xiàng):
alist = [1, 2, 3, 4] for x in addlist(alist):print(x)這的確是yield應(yīng)用的一個(gè)例子,但是,看過(guò)很多東西,并自己反復(fù)體驗(yàn)后,對(duì)yield有了一個(gè)全新的理解,其中這篇算是精品了。
包含yield的函數(shù)
假如你看到某個(gè)函數(shù)包含了yield,這意味著這個(gè)函數(shù)已經(jīng)是一個(gè)Generator,它的執(zhí)行會(huì)和其他普通的函數(shù)有很多不同。比如下面的簡(jiǎn)單的函數(shù):
def h():print('study yield')yield 5print('go on!')h()可以看到,調(diào)用h()之后,print 語(yǔ)句并沒(méi)有執(zhí)行!這就是yield。具體的內(nèi)容后面會(huì)越來(lái)越清晰,包括yield的工作原理。
yield是一個(gè)表達(dá)式
python 2.5以前,yield是一個(gè)語(yǔ)句,我也沒(méi)有考證,因?yàn)樵缍疾挥昧?#xff0c;現(xiàn)在yield是一個(gè)表達(dá)式:
m = yield 5表達(dá)式(yield 5)的返回值將賦值給m,所以,m = 5 肯定是錯(cuò)的。
那么如何獲取(yield 5)的返回值呢?需要用到send(msg)。
yield工作原理
揭曉yield的工作原理,需要配合next()函數(shù)。上面的h()被調(diào)用后并沒(méi)有執(zhí)行,因?yàn)樗衴ield表達(dá)式,通過(guò)next()可以恢復(fù)Generator執(zhí)行,直到下一個(gè)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()被調(diào)用后,h()開(kāi)始執(zhí)行,直到遇到y(tǒng)ield 5
因此輸出結(jié)果是:study yield
當(dāng)我們?cè)俅握{(diào)用next()時(shí),會(huì)繼續(xù)執(zhí)行,直到找到下一個(gè)yield。由于后面沒(méi)有yield了,因此會(huì)拋出異常:
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的函數(shù)執(zhí)行后,我們?cè)賮?lái)看另外一個(gè)非常重要的函數(shù)send(msg)。
其實(shí)next()和send()在一定意義上作用是相似的
區(qū)別
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) # 相當(dāng)于send(None) c.send('Fighting!') # (yield 5)表達(dá)式被賦予了'Fighting!'輸出的結(jié)果為:
study yield Fighting!注意 生成器剛啟動(dòng)時(shí)(第一次調(diào)用),請(qǐng)使用next()語(yǔ)句或是send(None),不能直接發(fā)送一個(gè)非None的值,否則會(huì)報(bào)TypeError,因?yàn)闆](méi)有yield語(yǔ)句來(lái)接收這個(gè)值。
send(msg) 與 next()的返回值
send(msg) 和 next() 的返回值比較特殊,是下一個(gè)yield表達(dá)式的參數(shù)(yield 5,則返回 5)。
到這里,第一個(gè)例子中,通過(guò)for i in alist 遍歷 Generator,其實(shí)是每次都調(diào)用了next(),而每次next()的返回值正是yield的參數(shù):
def s():print('study yield')m = yield 5print(m)d = yield 16print('go on!')c = s() s_d1 = next(c) # 相當(dāng)于send(None) s_d2 = c.send('Fighting!') # (yield 5)表達(dá)式被賦予了'Fighting!' print('My Birth Day:', s_d1, '.', s_d2)輸出結(jié)果:
study yield Fighting! My Birth Day: 5 . 16中斷Generator
上面的例子中,當(dāng)沒(méi)有可執(zhí)行程序的時(shí)候,會(huì)拋出一個(gè)StopIteration, 開(kāi)發(fā)過(guò)程中,中斷Generator是一個(gè)非常靈活的技巧
throw
通過(guò)拋出一個(gè)GeneratorExit異常來(lái)終止Generator。
close
close的作用和throw是一樣的,看它的源碼,可以發(fā)現(xiàn),它和raise一球樣
def throw(self, type, value=None, traceback=None):'''Used to raise an exception inside the generator.'''# 用于在生成器中拋出一個(gè)異常。passdef close(self):'''Raises new GeneratorExit exception inside the generator to terminate the iteration.'''# 在生成器中生成新的GeneratorExit異常來(lái)終止迭代。pass其實(shí)最后一個(gè)中斷生成器可以忽略的,在開(kāi)發(fā)過(guò)程中,不可避免的要用到這些,但是Python3內(nèi)部已經(jīng)做得很好了,一般不太需要手動(dòng)去做這件事情。
demo地址
https://github.com/seeways/PythonDemo/blob/master/static/yield_demo.py
總結(jié)
以上是生活随笔為你收集整理的深入理解 Python yield的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: GNU的C++代码书写规范,C语言之父D
- 下一篇: 大学里机器人比赛的那些事