天天看點

python權威指南

    讓你的python知識更上一次樓,推薦一個url:http://www.ibm.com/developerworks/cn/views/opensource/libraryview.jsp?sort_by=&show_abstract=true&show_all=&search_flag=&contentarea_by=Open+source&search_by=python&topic_by=-1&type_by=%E6%89%80%E6%9C%89%E7%B1%BB%E5%88%AB&ibm-search=%E6%90%9C%E7%B4%A2

清單 5. 使用 yield 的第四版
def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        yield b 
        # print b 
        a, b = b, a + b 
        n = n + 1 

'''      

第四個版本的 fab 和第一版相比,僅僅把 print b 改為了 yield b,就在保持簡潔性的同時獲得了 iterable 的效果。

調用第四版的 fab 和第二版的 fab 完全一緻:

>>> for n in fab(5): 
 ...     print n 
 ... 
 1 
 1 
 2 
 3 
 5      

簡單地講,yield 的作用就是把一個函數變成一個 generator,帶有 yield 的函數不再是一個普通函數,Python 解釋器會将其視為一個 generator,調用 fab(5) 不會執行 fab 函數,而是傳回一個 iterable 對象!在 for 循環執行時,每次循環都會執行 fab 函數内部的代碼,執行到 yield b 時,fab 函數就傳回一個疊代值,下次疊代時,代碼從 yield b 的下一條語句繼續執行,而函數的本地變量看起來和上次中斷執行前是完全一樣的,于是函數繼續執行,直到再次遇到 yield。

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

清單 6. 執行流程
>>> f = fab(5) 
 >>> f.next() 
 1 
 >>> f.next() 
 1 
 >>> f.next() 
 2 
 >>> f.next() 
 3 
 >>> f.next() 
 5 
 >>> f.next() 
 Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
 StopIteration      

當函數執行結束時,generator 自動抛出 StopIteration 異常,表示疊代完成。在 for 循環裡,無需處理 StopIteration 異常,循環會正常結束。

我們可以得出以下結論:

一個帶有 yield 的函數就是一個 generator,它和普通函數不同,生成一個 generator 看起來像函數調用,但不會執行任何函數代碼,直到對其調用 next()(在 for 循環中會自動調用 next())才開始執行。雖然執行流程仍按函數的流程執行,但每執行到一個 yield 語句就會中斷,并傳回一個疊代值,下次執行時從 yield 的下一個語句繼續執行。看起來就好像一個函數在正常執行的過程中被 yield 中斷了數次,每次中斷都會通過 yield 傳回目前的疊代值。

yield 的好處是顯而易見的,把一個函數改寫為一個 generator 就獲得了疊代能力,比起用類的執行個體儲存狀态來計算下一個 next() 的值,不僅代碼簡潔,而且執行流程異常清晰。