零零碎碎學着python已經好久了,但是總會發現有好些知識點都沒學到,今天在牛客網看到了這樣一道題:
# 下列代碼的運行結果是什麼?
def bar(n):
m = n
while True:
m+=1
yield m
b = bar(3)
print b.next()
題目本身沒什麼難度,所以就不多說了~
emmmm…剛看到題目的時候,果然發現了我印象裏沒有的東西——yield。迅速查了資料,大概瞭解到,一般情況下,帶有yield的函數不是一般的函數,而是一個生成器(generator)。
yield這個關鍵字的作用比較類似於return和print,均有輸出/返回結果之意,next()表示依次返回迭代結果。
下面,暫且不說generator,先以一個簡單的例子——裴波那契數列來說一說yield。
實例1:簡單輸出裴波那契數列的前n個數。
def fibo(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a + b
n = n + 1
這樣的寫法屬於常規操作,但是,函數的可複用性不高。因爲fibo()函數沒有返回值,其他函數無法獲得該函數生成的數列。
這時我們會想到,爲了提高函數的可複用性,我們不直接打印出數列,而是去返回一個List.
從而也就得到了下面這種做法。
實例2:
def fibo(max):
n, a, b = 0, 0, 1
L = []
while n < max:
L.append(b)
a, b = b, a + b
n = n + 1
return L
通過以上的改動,函數的可複用性大大提高,但是,該函數在運行中佔用的內存會隨着參數 max 的增大而增大,因此如果要控制內存佔用,最好不要用 List來保存中間結果,而是通過 iterable 對象來迭代。
for i in range(1000):
pass
# 會生成一個有1000個元素的List
for i in xrange(1000):
pass
# 不會生成一個 1000 個元素的 List,而是在每次迭代中返回下一個數值,內存空間佔用很小。因爲 xrange 不返回 List,而是返回一個 iterable 對象。
利用 iterable 我們可以把 fibo() 函數改寫爲一個支持 iterable 的 class,這樣也就有了下面的實例:
實例3:
class Fibo(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 += 1
return r
raise StopIteration()
for n in Fibo(5):
print n
Fibo 類通過 next() 函數不斷返回數列的下一個數,內存佔用始終爲常數、
然而,
使用 class 改寫的這個版本,代碼遠遠沒有實例1簡潔。如果我們既要保持實例1的簡潔性,又要獲得 迭代效果,這時,yield 就派上用場了:
實例4:
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
這時你就會發現,實例4與實例1的區別僅僅在於將中間的print換成了yield。
因此,簡單來說,yield就是把一個普通函數變成了一個生成器。
yield 的好處是顯而易見的,把一個函數改寫爲一個 generator 就獲得了迭代能力,比起用類的實例保存狀態來計算下一個 next() 的值,不僅代碼簡潔,而且執行流程異常清晰。
以上大致是今日所得,日後如果遇到相關問題,再對此文另行補充~