python使用yield來減少記憶體開銷
阿新 • • 發佈:2019-01-05
本文參考自:http://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/
以斐波那契數列的實現來說明這個問題:
demo1:
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
for n in fab(5):
print n
一般來講,我們都會寫成上邊程式的樣子,這樣既實現了基本功能,又可以複用,基本符合要求了。
但是如果max是1000w或者更大呢,那麼儲存在list中將會佔據很大的記憶體。常見的情況還包括一下消耗大量記憶體的做法:
上面兩個小對比,是我們平時寫程式時不是很注意的地方,但是當你讀到這之後,以後寫程式不光要考慮怎麼實現,而且要寫出更優秀的程式碼來。for line in open("test.txt").readlines(): print line #這種做法是將檔案的中的內容一次全部讀取到記憶體中,每次從記憶體中取出一行然後輸出,如果檔案內容太大,將消耗大量的記憶體。 for line in open("test.txt"): print line #這種做好就好多了,既簡單又不節省記憶體,利用迭代器來每次讀取一行資料。 for i in range(1000): pass #會產生一個1000個元素的list for i in xrange(1000): pass #xrange()產生的是一個迭代物件,而不是一個1000個元素的list, 每次迭代返回下一個數值
然後繼續我們的斐波那契數列:
demo2:
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() <pre class="displaycode">for n in Fab(5): print n
上面這個版本基本達到了我們的要求,實現基本功能+節省記憶體,利用函式next來實現每次輸出下一個值, 但是這麼小小的一個功能我們竟然寫了這麼多的程式碼,不夠簡潔!!!
demo3:
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
<pre name="code" class="python"><pre class="displaycode">for n in fab(5):
print n
簡單來講:yield 的作用就是把一個函式變成一個 generator,帶有 yield 的函式不再是一個普通函式,Python 直譯器會將其視為一個 generator,呼叫 fab(5) 不會執行 fab 函式,而是返回一個 iterable 物件!在 for 迴圈執行時,每次迴圈都會執行 fab 函式內部的程式碼,執行到 yield b 時,fab 函式就返回一個迭代值,下次迭代時,程式碼從 yield b 的下一條語句繼續執行,而函式的本地變數看起來和上次中斷執行前是完全一樣的,於是函式繼續執行,直到再次遇到
yield。(照抄的參考文章)
個人理解: 是不是就是實時呼叫的意思,什麼時候執行next什麼時候執行一次fab函式,但是上一次的值都在記憶體中儲存。而不是一次將所有的結果求出放在list中。
更多細節請參考本文一開頭給出的文章連結, 下面我再補充一下關於迭代器和生成器的理解:
生成器:生成器(Generator)是建立迭代器的簡單而強大的工具
迭代器:
1、對於無法隨機訪問的資料結構(比如set)而言,迭代器是唯一的訪問元素的方式
2、它不要求你事先準備好整個迭代過程中所有的元素。迭代器僅僅在迭代至某個元素時才計算該元素,而在這之前或之後,元素可以不存在或者被銷燬。這個特點使得它特別適合用於遍歷一些巨大的或是無限的集合,比如幾個G的檔案,或是斐波那契數列等等。
具體的理解以及內部是如何工作的,大體可以參照:http://www.jb51.net/article/73939.htm
如果想更身體的瞭解迭代協議等內容,可以自行學習。