生成器是迭代器,同時也并不僅僅是迭代器,不過迭代器之外的用途實在是不多,所以我們可以大聲地說:生成器提供了非常方便的自定義迭代器的途徑。
這是函數式編程指南的最后一篇,似乎拖了一個星期才寫好,嗯……
1. 生成器(generator)
1.1. 生成器簡介
首先請確信,生成器就是一種迭代器。生成器擁有next方法并且行為與迭代器完全相同,這意味著生成器也可以用于Python的for循環中。另外,對于生成器的特殊語法支持使得編寫一個生成器比自定義一個常規的迭代器要簡單不少,所以生成器也是最常用到的特性之一。
從Python 2.5開始,[PEP 342:通過增強生成器實現協同程序]的實現為生成器加入了更多的特性,這意味著生成器還可以完成更多的工作。這部分我們會在稍后的部分介紹。
1.2. 生成器函數
1.2.1. 使用生成器函數定義生成器
如何獲取一個生成器?首先來看一小段代碼:
>>> def get_0_1_2():... yield 0... yield 1... yield 2...>>> get_0_1_2<function get_0_1_2 at 0x00B2CB70>
我們定義了一個函數get_0_1_2,并且可以查看到這確實是函數類型。但與一般的函數不同的是,get_0_1_2的函數體內使用了關鍵字yield,這使得get_0_1_2成為了一個生成器函數。生成器函數的特性如下:
調用生成器函數將返回一個生成器;
>>> generator = get_0_1_2()>>> generator<generator object get_0_1_2 at 0x00B1C7D8>
第一次調用生成器的next方法時,生成器才開始執行生成器函數(而不是構建生成器時),直到遇到yield時暫停執行(掛起),并且yield的參數將作為此次next方法的返回值;
>>> generator.next()0
之后每次調用生成器的next方法,生成器將從上次暫停執行的位置恢復執行生成器函數,直到再次遇到yield時暫停,并且同樣的,yield的參數將作為next方法的返回值;
>>> generator.next()1>>> generator.next()2
如果當調用next方法時生成器函數結束(遇到空的return語句或是到達函數體末尾),則這次next方法的調用將拋出StopIteration異常(即for循環的終止條件);
>>> generator.next()Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration
生成器函數在每次暫停執行時,函數體內的所有變量都將被封存(freeze)在生成器中,并將在恢復執行時還原,并且類似于閉包,即使是同一個生成器函數返回的生成器,封存的變量也是互相獨立的。
我們的小例子中并沒有用到變量,所以這里另外定義一個生成器來展示這個特點:
>>> def fibonacci():... a = b = 1... yield a... yield b... while True:... a, b = b, a+b... yield b...>>> for num in fibonacci():... if num > 100: break... print num,...1 1 2 3 5 8 13 21 34 55 89
看到while True可別太吃驚,因為生成器可以掛起,所以是延遲計算的,無限循環并沒有關系。這個例子中我們定義了一個生成器用于獲取斐波那契數列。
1.2.2. 生成器函數的FAQ
接下來我們來討論一些關于生成器的有意思的話題。
你的例子里生成器函數都沒有參數,那么生成器函數可以帶參數嗎?
當然可以啊親,而且它支持函數的所有參數形式。要知道生成器函數也是函數的一種:)
新聞熱點
疑難解答