怎麼用python實作一個斐波那契數列?新手們很容易這樣寫:
def fab(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a+b
n += 1
這樣可以正确的獲得斐波那契數列,但是在函數中,用print列印數字導緻函數複用性較差。因為fab函數沒有傳回值,其他函數沒法得到通過fab函數産生的數列。
針對這個問題的一個改進辦法,就是不直接列印出來,而是傳回一個List,于是可以寫成這樣:
def fab(max):
n, a, b = 0, 0, 1
result = list()
while n < max:
result.append(b)
a, b = b, a+b
n += 1
return result
然後通過以下方式列印出list中的數字:
for n in fab(5):
print n
這樣增加了函數的複用性,但随之而來另一個問題,記憶體占用會随着max的值增大而增大,如果要控制記憶體占用,不推薦使用List。
要儲存中間結果,可以通過iterable對象來疊代。例如:
for x in range(1000): pass
上面這段代碼會生成一個含1000個元素的list,而:
for x in xrange(1000): pass
則不會生成一個List,而是在在每次疊代時傳回下一個數值,記憶體占用空間很小。xrange傳回的就是一個iterable對象。
是以,針對上面的問題,可以把fab函數改進成為一個支援iterable的class。
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 += 1
return r
raise StopIteration()
Fab類通過調用next()函數不斷傳回數列中的下一個數,記憶體占用始終是一個常數。
for n in Fab(5):
print n
但是這段代碼和之前的代碼比較起來,顯得沒那麼簡潔。為了儲存代碼的簡潔,又要獲得iterable的效果,yield就派上用場了。
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a+b
n += 1
這段代碼隻是把第一段代碼中的print b改成了yield b,就有了iterable的效果。調用方式為:
for n in fab(5):
print n
此處,yield的作用就是把一個函數變成一個generator,帶有yield的函數不再是一個普通函數,python解釋器會将其視為一個generator,調用fab(5)不會執行fab函數,而是傳回一個可疊代對象。在for循環執行時,每次循環都會執行fab函數内部的代碼,執行到yield b時,fab函數就傳回一個疊代值,下次疊代時,就會從yield b的下一條語句繼續執行,函數中的本地變量和上次中斷執行前是完全一樣的,于是函數繼續執行,直到再次遇到yield。
可以通過手動調用fab(5)的next()方法,看到具體的執行流程。
>>> 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異常,程式會正常結束。