孫行者定身術+時間機器-Generator in Python(正文)

書接上回,爲什麼需要coroutine?我們採用一個經典而淺顯的案例:計算Fibnacci數列。翠花,上代碼(C代碼):

void get_fib(int n, int* result)

{
int i = 0;
for (i = 0; i < n; i++)
{
if (i < 2)
{
result[i] = i;
continue;
}
result[i] = result[i - 1] + result[i - 2];
}
}

上述代碼中,參數n指定生成Fibnacci數字的個數,result爲存放結果的buffer。這種寫法是常見套路之一。我不必細說你就可以想象出調用處是怎樣使用這個函數的。這是傳統函數的套路。它有一個比較煩人的地方,就是存儲空間的管理。如果數列規模n比較大,你不得不很不情願地準備一個很大的空間用於存儲,還可能一直提心吊膽地擔心內存溢出或泄漏。

OK, 現在該輪到generator出場了。翠花,上代碼(Python):

函數定義處:
def get_fib():

a, b = 0, 1

while 1:

yield b

a, b = b, a+b

函數調用處:

fib = get_fib()

for num in fib:

print num

注意,在這裏你看不到準備存儲空間的代碼,因爲確實沒有這個動作。同時,yield登臺亮相了。

Python版的這個”虛僞”的函數get_fib,在調用語句“fib = get_fib()”中,它其實並未被執行,而是構造並返回了一個被稱爲generator的對象,然後它被用在接下來的for循環中,用於枚舉和打印fibnacci數(想要多少要多少,直到你主動退出循環)。按照官方的說法,這個generator的本質是一個迭代器(iterator),一個對象,它暗含了一個next()方法,該方法在for循環中被隱式調用。for循環每調用一次next方法,get_fib()函數的代碼才被真正執行一次,但執行中遇到yield語句後就掛起,緊隨yield其後的表達式b被作爲本次返回值返回給調用者,本次運行結束。下一次next()再被調用,再從凍結處繼續執行。也就是說,每次運行它都只運行函數體的一部分而不是全部,並且能夠自動記憶前次停止的位置以便下一次從那裏繼續執行。

該怎樣來認識generator的本質呢?其實,它就像是一個被動吐泡泡的機器,你碰它一下(調用next()方法),它就吐一個泡泡,不碰則不吐。我想這就是其名稱“generator”的來歷吧,因爲它就負責不斷地generate數據給調用者。因此,它不需要事先準備好一大塊空間存放一堆泡泡,任意時刻它只產生一個泡泡!!!Python的官方文檔說,generator是一個輕量級的coroutine實現。Wiki中的coroutine詞條說coroutine是一個有多個入口、可以多次掛起和恢復執行的subroutine。這兩個解釋,和你在本例中看到的完全一致。按我個人的理解,在應用層面上看,generator是採用了時間換空間的策略,即用多次執行的代價換取了節省大片存儲空間的好處。對於Fibnacci這個特別簡單的例子,如果在C版本中增加若干局部static變量應該可以近似地模擬出來。

那麼,這樣的時空互換策略真的值得嗎?有普遍應用的意義嗎?這也是我懷疑的一點。別跟我一樣孤陋寡聞。按照Wiki以及有識者的介紹,generator非常實用且應用非常廣泛甚至可用在系統編程級別,例如合作性任務(生產者消費者問題)、迭代器、無窮列表(你沒法事先準備足夠大的空間)、實現pipe。而且,表面上就有一個顯著的優勢:你不必等所有數據都準備好纔開始處理,有一個就處理一個。在讀取特別大的文件時,generator也是非常好用的手法。有一位貌似比較資深的工程師提到,利用generator可以很容易地實現Unix編程中非常典型的pipeline架構,有興趣深入的話,我建議你看看這裏

後記

我知道即使我這樣仔細的舉例說明,第一次接觸它的人還是不一定能夠真的理解。因爲這裏邊的確有編程理念上的衝擊。你不能抱着對傳統函數的理解不放,這將成爲理解generator和coroutine的最大障礙。不過,不要灰心,一次不行就多來幾次,可以在網絡上多看些相關文章(我建議你儘量看英文的或limodou的解釋),或者來找我聊聊,我可以給你演示一些更生動的例子。

你可能會不屑於花費時間和精力來學些這麼一個看似怪誕的東西,但是,我親身的體會是,這寶貝真的值得你花時間來學習一下,即使你是一個老傢伙。學習它將會非常有益於擴展你的技術視野以及編程理念,是一種顯著的提高。真的,我不忽悠。等你掌握了定身術和時間機器的時候,你一定得請我吃一頓。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章