日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

學 Python 怎能不知 yield ?

 風聲之家 2021-03-16

簡說Python 1周前

以下文章來源于無量測試之道 ,作者無量測試之道


 簡說Python推薦 
來源|無量測試之道
作者|無量測試之道

引言

你可能聽說過,帶有 yield 的函數(shù)在 Python 中被稱之為 generator(生成器),又或者都沒關(guān)注過,Python 中還有個 yield 的存在。如果你了解過 Python 中的 yield,那你知道何謂 generator 嗎?

yield 的概念

理解yield 的 generator 概念,首先以一個常見的編程題目來展示 yield 的概念。

如何生成斐波那契數(shù)列

斐波那契(Fibonacci)數(shù)列是一個非常簡單的遞歸數(shù)列,除第一個和第二個數(shù)外,任意一個數(shù)都可由前兩個數(shù)相加得到。用計算機程序輸出斐波那契數(shù)列的前 N 個數(shù)是一個非常簡單的問題,有些 Python 基礎(chǔ)的小伙伴都可以輕易寫出如下函數(shù):

第 1 版本:簡單輸出斐波那契數(shù)列前 N 個數(shù)

def createNum(count): 
n, a, b = 0, 0, 1
while n < count:
print b
a, b = b, a + b
n = n + 1
createNum(5)

執(zhí)行以上代碼,我們可以得到如下輸出:

1 
1
2
3
5

輸出結(jié)果是沒有問題的,但是版本 1 中的寫法是直接在 createNum 函數(shù)中用 print 打印數(shù)字會導致該函數(shù)可復用性較差,因為 createNum 函數(shù)返回 None,其他函數(shù)無法獲得該函數(shù)生成的數(shù)列。

要提高 createNum 函數(shù)的可復用性,最好不要直接打印出數(shù)列,而是返回一個 List。以下是 createNum 函數(shù)改寫后的第二個版本:

第 2 版本:輸出斐波那契數(shù)列前 N 個數(shù)

def createNum(count): 
n, a, b = 0, 0, 1
L = []
while n < count:
L.append(b)
a, b = b, a + b
n = n + 1
return L

for n in createNum(5):
print n

該版本中 createNum 函數(shù)返回的 List的結(jié)果如下:

1 
1
2
3
5

改寫后的 createNum 函數(shù)通過返回 List 能滿足復用性的要求,但是與此同時也會存在一個明顯的問題是:該函數(shù)在運行中占用的內(nèi)存會隨著參數(shù) count 的增大而增大,如果要控制內(nèi)存占用,最好不要用 List 來保存中間結(jié)果,而是通過 iterable 對象來迭代。在每次迭代中返回下一個數(shù)值,如此:內(nèi)存空間占用很小。因為是直接返回一個 iterable 對象。

第 3 版本:使用 yield 輸出斐波那契數(shù)列前 N 個數(shù)

def createNum(count): 
n, a, b = 0, 0, 1
while n < count:
yield b # 使用 yield
# print(b)
a, b = b, a + b
n = n + 1

for n in createNum(5):
print n

也可以手動調(diào)用 createNum(5) 的 next() 方法(因為 createNum(5) 是一個 generator 對象,該對象具有 next() 方法),這樣我們就可以更清楚地看到 createNum 的執(zhí)行流程:

第 4 版本:執(zhí)行流程

def createNum(count): 
n, a, b = 0, 0, 1
while n < count:
yield b # 使用 yield
# print(b)
a, b = b, a + b
n = n + 1


#使用for循環(huán)來執(zhí)行createNum()函數(shù),它返回一個迭代值,下次迭代從yield語句的下一條語句繼續(xù)執(zhí)行
<!--for n in createNum(5):
print n-->

#使用next方法來執(zhí)行createNum()函數(shù),generator(生成器)對象具有next()方法
num = createNum(5)
print(next(num))
print(next(num))
print(next(num))
print(next(num))
print(next(num))
print(next(num))

運行以上代碼,結(jié)果輸出如下:

1
1
2
3
5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

由輸出結(jié)果可發(fā)現(xiàn)在執(zhí)行第 6 個 print(next(num)) 時拋出一個 StopIteration 的異常,是因為在第 5 個 print(next(num)) 執(zhí)行完時函數(shù)已經(jīng)結(jié)束,再執(zhí)行第 6 個print(next(num))時,generator 自動拋出 StopIteration 異常,表示迭代完成。在 for 循環(huán)里,無需處理 StopIteration 異常,循環(huán)會正常結(jié)束。

yield 的作用

簡單地講,yield 的作用就是把一個函數(shù)變成一個 generator,帶有 yield 的函數(shù)不再是一個普通函數(shù),Python 解釋器會將其視為一個 generator,調(diào)用 createNum(5) 不會執(zhí)行 createNum 函數(shù),而是返回一個 iterable 對象!

在 for 循環(huán)執(zhí)行時,每次循環(huán)都會執(zhí)行 createNum 函數(shù)內(nèi)部的代碼,執(zhí)行到 yield b 時,createNum 函數(shù)就會返回一個迭代值,下次迭代時,代碼從 yield b 的下一條語句繼續(xù)執(zhí)行,而函數(shù)的本地變量看起來和上次中斷執(zhí)行前是完全一樣的,于是函數(shù)繼續(xù)執(zhí)行,直到再次遇到 yield。

yield 使用場景

  • 迭代生成數(shù)據(jù)(生產(chǎn)者,數(shù)據(jù)量巨大時優(yōu)勢更加明顯,它可以不占用大量內(nèi)存)

  • 接收數(shù)據(jù)(消費者)

  • 中斷(協(xié)作式的任務(wù))


總結(jié)

一個帶有 yield 的函數(shù)就是一個 generator,它和普通函數(shù)不同,生成一個 generator 看起來像函數(shù)調(diào)用,但不會執(zhí)行任何函數(shù)代碼,直到對其調(diào)用 next()(在 for 循環(huán)中會自動調(diào)用 next())才開始執(zhí)行。雖然執(zhí)行流程仍按函數(shù)的流程執(zhí)行,但每執(zhí)行到一個 yield 語句就會中斷,并返回一個迭代值,下次執(zhí)行時從 yield 的下一個語句繼續(xù)執(zhí)行??雌饋砭秃孟褚粋€函數(shù)在正常執(zhí)行的過程中被 yield 中斷了數(shù)次,每次中斷都會通過 yield 返回當前的迭代值。

yield 的好處是顯而易見的,把一個函數(shù)改寫為一個 generator 就獲得了迭代能力,比起用類的實例保存狀態(tài)來計算下一個 next() 的值,不僅代碼簡潔,而且執(zhí)行流程異常清晰。


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多