帶有yield的函數在Python中被稱之為generator(生成器),下面我們將使用斐波那契數列來舉例說明下該函數:(環境是在Python3.x下)
如何生成斐波那契數列:
斐波那契(Fibonacci)數列是一個簡單的遞歸數列,除第一個數和第二個數外,任意一個數都可由前兩個數相加得到。用計算機程序輸出斐波那契數列的前N個數是一個非常簡單的問題:
- 版本一:簡單輸出斐波那契數列前N個數
# -*- coding: utf-8 -*- # Time : 2019/5/28 14:25 # Author : Eric # FileName: yield使用淺析.py # Software: PyCharm #----------------------------------------------------------------------------------------------------------------------- def fab(max): n, a, b = 0, 0, 1 while n < max: print(b) a, b = b, a + b n = n + 1 #執行fab(10),我們可以得到如下的輸出: print(fab(10)) 1 1 2 3 5 8 13 21 34
55 None
結果是沒有問題,但是有經驗的開發者會指出,直接在fab函數中有print打印數字會導致該函數可復用性較差,因為fab函數返回None,其他函數無法獲得該函數生成的數列。
要提高fab函數的復用性,最好不要直接打印出數列,而是返回一個列表(list)。以下是fab函數改寫后的第二個版:
- 版本二:輸出斐波那契數列前N個數
def fab(max): n, a, b = 0, 0, 1 L = [] while n < max: L.append(b) a, b = b, a + b n = n + 1 return L #可以使用如下方式打印出fab函數返回的List: for n in fab(10): print(n) 1 1 2 3 5 8 13 21 34 55
改寫后的fab函數通過返回List能滿足復用性的要求,但是更有經驗的開發者會指出,該函數在運行中占用的內存會隨着參數max的增大而增大,如果要控制內存占用,最好不用要List來保存中間結果。
- 版本三: 使用創建類的的方法來實現
class Fab(object): def __init__(self,max): self.max = max self.n, self.a, self.b = 0, 0, 1 def __iter__(self): return self def __next__(self): if self.n < self.max: r = self.b self.a, self.b = self.b, self.a + self.b self.n = self.n + 1 return r raise StopIteration() #Fab類通過__next__()不斷返回數列的下一個數,內存占用始終為常數: for n in Fab(5): print(n) 1 1 2 3 5
然而,使用class改寫的這個版本,代碼遠遠沒有第一版的fab函數來得簡潔。如果我們想要保持第一版fab函數的簡潔性,同時又要獲得iterable的效果,yield就派上用場了。
- 版本四:使用yield的第四版
def fab(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 #調用第四版的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時,返回函數就會返回一個迭代值,下次迭代時,代碼從yield的下一條語句繼續執行,而函數的本地變量看起來和上次中斷執行前是完全一樣的,於是函數繼續執行,直到再次遇到yield。
yield的好處是顯而易見的,把一個函數改寫為一個generator就獲得了迭代的能力,比起用類的實例保存狀態來計算下一個next()的值,不僅代碼簡介,而且執行流程異常清晰。
如何判斷一個函數是否是一個特殊的generator函數?可以利用isgeneratorfunction判斷:
from inspect import isgeneratorfunction print(isgeneratorfunction(fab)) True
要注意區分fab和fab(5),fab是一個generatorfunction,而fab(5)是調用fab返回的一個generator,好比類的定義和類的實例的區別:
類的定義和類的實例:
import types print(isinstance(fab,types.GeneratorType)) print(isinstance(fab(5),types.GeneratorType)) False True
fab是無法迭代的,而fab(5)是可迭代的:
from collections import Iterable print(isinstance(fab,Iterable)) print(isinstance(fab(5),Iterable)) False True
return的作用
在一個generator function中,如果沒有return,則默認執行至函數完畢,如果在執行過程中return,則直接拋出StopIteration終止迭代。