生成器是python中一个非常酷的特性,python 2.2中引入后在2.3变成了标准的一部分。它能够让你在许多情况下以一种优雅而又更低内存消耗的方式简化无界(无限)序列相关的操作。
生成器是可以当做iterator使用的特殊函数,它功能的实现依赖于关键字yield,下面是它如何运作一个简单的演示:
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 函数的实现:
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实现:
# -*- coding: utf-8 -*-
# filename: fib.py
def fib():
first = 0
second = 1
yield first
yield second
while 1 :
next = first + second
yield next
first = second
second = next
运行:
>>> fib()
< generator object fib at 0xb76fcfcc >
>>> import itertools
>>> list(itertools.islice(fib(), 10 ))
[0, 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 ]
也可以用如下方式截取一部分输出(但建议使用 itemtools 模块):
... print num
上述 fibonacci 函数的实现看起来较为繁琐,更加简捷优雅的实现如下:
# -*- coding: utf-8 -*-
# filename: fib.py
def fib():
first, second = 0, 1
while 1 :
yield second
first, second = second, first + second
注意:但是在C语言中由于static的特性,在一个函数体内当需要产生多组 fibonacci 数列时可能就需要定义相同的诸如fib1() fib2() fib3()的函数。python中可以利用上述函数构造任意多个独立的生成器对象。
至于生成器(generator)应该使用在哪些方面以便更好地节省内存——你可以使用在需要计算列表的值但是每次只需要访问一个item的地方,和预先计算好将其存储在一个列表里相比,生成器在运行中逐个计算其值(computes the values on the fly)。