带有yield的函数在Python中被称为generator(生成器)。
yield演变过程
生成斐波拉切数列的例子说明.
清单1
直接用print,函数没有可复用性差。
def fab(max):
n,a,b=0,0,1
while n<max:
print b
a,b=b,a+b
n=n+1
#输出
>>>fab(5)
>1
>1
>2
>3
>5
清单2
解决复用性差的问题,可以使用数列。但是运行内存会随着max增加而增加。
def fab(max):
n,a,b=0,0,1
L=[]
while n <max:
L.append(n)
a,b=b,a+b
n=n+1
return L
#打印输出结果
>>>for n in fab(5):
print n
1
1
2
3
5
其中输出最好不要使用List,而是通过中间结果,iterable对象来迭代:
for i in range(1000):pass#会生成一个1000个元素的List
for i in xrange(1000):pass#不会生成一个1000个元素的List
清单3
将fab函数改写成一个支持iterable的class。
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():
if self.n<self.max:
r=self.b
self.a,self.b=self.b,self.a+self.b
self.b=self.n+1
return r
rasise StopInteration()
#内存占用一直只是个常数
>>>for n in Fab(5):
print n
1
1
2
3
5
清单4
为了更简洁,yield派上用场了。
def fab(max):
n,a,b=0,0,1
while n<max:
yield b
a,b=b,a+b
n=n+1
#输出
>>>for n in fab(5):
print n
1
1
2
3
5
#也可以手动执行流程
>>>f=fab(5)
>>>f.next()
1
>>>f.next()
1
>>>f.next()
2
>>>f.next()
3
>>>f.next()
5
>>>f.next()
Trackback (most recent call last):
File "<stdin>",line 1,in <modue>
StopIteration
判断函数是否是一个特殊的迭代器generaor函数
注意:要区分fab和fab(5),fab是一个generator function,而fab(5)是调用fab返回的一个generator,好比类的定义和类的实例的区别.:
>>>from inspect import isgeneratorfunction
>>>isgeneratorfunction(fab)
True
>>>import types
>>>isinstance(fab,types.GeneratorType)
False
>>>isInstance(fab(5),types.GeneratorType)
True
#fab无法迭代,而fab(5)是可以迭代的
>>>from collections import Iterable
>>>isinstance(fab,Iterable)
False
>>>isinstance(fab(5),Iterable)
True
#每次调用fab函数都会生成一个新的generator实例,各个实例互相不影响
>>>f1=fab(3)
>>>f2=fab(5)
>>>print 'f1:',f1.next()
f1:1
>>>print 'f2:',f2.next()
f2:1
>>>print 'f1:',f1.next()
f1:1
>>>print 'f2:',f2.next()
f2:1
>>>print 'f1:',f1.next()
f1:2
>>>print 'f2:',f2.next()
f2:2
>>>print 'f2:',f2.next()
f2:3
>>>print 'f2:',f2.next()
f2:5
return 的作用
在一个generator function中,如果没有return,则默认执行至函数完毕,如果在执行过程中return ,直接抛出StopIteration终止迭代.
另一个读取文件例子
如果直接对文件对象调用read()方法,会导致不可预测的内存占用。好的方法是 利用固定长度缓冲区不断读取文件内容。
def read_file(fpath):
BLOCK_SIZE=1024
with open('/local/text.txt') as f:
block=f.read(BLOCK_SIZE)
if block:
yield block
else:
return