產生器是python中一個非常酷的特性,python
2.2中引入後在2.3變成了標準的一部分。它能夠讓你在許多情況下以一種優雅而又更低記憶體消耗的方式簡化無界(無限)序列相關的操作。
產生器是可以當做iterator使用的特殊函數,它功能的實現依賴於關鍵字yield,下面是它如何運作一個簡單的示範:
>>>def spam():
yield"first"
yield"second"
yield"third"
>>> spam
<function spam at 0x011F32B0>
>>>for x in spam():
print x
first
second
third
>>> gen=spam()
>>> gen
<generator object spam at 0x01220B20>
>>> gen.next()
'first'
>>> gen.next()
'second'
>>> gen.next()
'third'
在函數spam()內定義了一個產生器,但是對spam()的調用永遠只能獲得一個單獨的產生器對象,而不是執行函數裡面的語句,這個對象(generator object)包含了函數的原始代碼和函數調用的狀態,這狀態包括函數中變數值以及當前的執行點——函數在yield語句處暫停(suspended),返回當前的值並儲存函數的調用狀態,當需要下一個條目(item)時,可以再次調用next,從函數上次停止的狀態繼續執行,知道下一個yield語句。
產生器和函數的主要區別在於函數 return a value,產生器 yield a value同時標記或記憶 point
of the yield 以便於在下次調用時從標記點恢複執行。 yield
使函數轉換成產生器,而產生器反過來又返回迭代器。
有三種方式告訴迴圈產生器中沒有更多的內容:
- 執行到函數的末尾("fall
off the end")
- 用一個return語句(它可能不會返回任何值)
- 拋出StopIteration異常
一個經典的例子是和C語言中的static語句相比較:在Python沒有明確支援的所謂static變數,但是在函數之間相互調用時,產生器能讓你能以一個更優雅的方式實作類別似的效果:
在C語言中
fibonacci 函數的實現:
#include <stdio.h>
int main() {
printf("0\n");
printf("1\n");
while (1)
printf("%d\n",fib());
/* for test use*/
// int i=0;
// while(i<20){
// i++;
// printf("%d\n", fib());}
}
int fib() {
static unsigned first=0,second=1,next,retval;
next=first+second;
retval=next;
first=second;
second=next;
return retval;
}
python實現:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# filename: fib.py
def fib():
first=0
second=1
yield first
yield second
while1:
next=first+second
yield next
first=second
second=next
運行:
>>>from fib import fib
>>> fib()
<generator object fib at 0xb76fcfcc>
>>>import itertools
>>> list(itertools.islice(fib(), 10))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
也可以用如下方式截取一部分輸出(但建議使用 itemtools 模組):
>>>for (i, num) in zip(range(10),fib()):
... print num
上述
fibonacci 函數的實現看起來較為繁瑣,更加簡捷優雅的實現如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# filename: fib.py
def fib():
first, second=0, 1
while1:
yield second
first, second= second, first+second
注意:但是在C語言中由於static的特性,在一個函數體內當需要產生多組
fibonacci 數列時可能就需要定義相同的諸如fib1() fib2() fib3()的函數。python中可以利用上述函數構造任意多個獨立的產生器對象。
至於產生器(generator)應該使用在哪些方面以便更好地節省記憶體——你可以使用在需要計算資料行表的值但是每次只需要訪問一個item的地方,和預先計算好將其儲存在一個列表裡相比,產生器在運行中逐個計算其值(computes the values on the fly)。