通常在for...in...循环中,in后面是一个数组,这个数组是一个可迭代对象,类似的还有字符串,文件及其他可迭代对象。这种方式的缺陷是数据都存在内存中,如果数据非常庞大的话会非常消耗内存的。因此在Python可以使用生成器(generator)来迭代较大数据。生成器是在用的时候才生成,如:gen = (x*x for i in range(10)) ,注意这里用的是()。生成器(generator)可以迭代的关键是有一个next()方法,工作原理就是通过重复的条用next()方法,直到捕获到异常(StopIteration)。
那么什么是yield?
带有yield的函数不在是一个普通的函数而是一个生成器(generator)。yield是一个类似return的关键字,迭代一次遇到yield就返回,不同return的是,下次迭代将从上次遇到yield的后面开始执行,简单的说就是:yield就是return一个值,并记住这个返回的位置,下次迭代就从这个位置开始。
例子1:普通斐波那契数列
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
值越大内存占用也会增大所以最好不要用list,可以通过iterable对象迭代
列子2:iterable对象迭代(Python2.x)
for i in range(1000): pass
for i in xrange(1000): pass
range会生成一个1000个元素的list,而xrange不会生成list,返回的是一个iterable对象,是在每次迭代的时候返回下一个值,内存占用会很小。
我们可以利用iterable写一个class实现斐波那契数列
例子3:
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(self):
if self.n < self.max:
r = self.b
self.a, self.b = self.b, self.a + self.b
self.n = self.n + 1
return r
raise StopIteration()
for n in Fab(5):
print n
Fab类通过next()不断返回数列的下一个数,内存占用始终为常数:
结果:
1
1
2
3
5
使用class虽然可以节省内存,但代码并不简洁,那么如何保证代码简洁又能实现iterable。答案自然是yield。
例子4:
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
yield不仅仅用于for循环中,也可以用于某个函数的参数,只要这个函数的参数允许迭代。如:array.extend
yield中最重要的函数是send()和next()。
send和next区别在于send可以传递参数给yield表达式,传递的参数会作为yield表达式的值。就是可以修改yield表达式的值,如:a = yield 10 第一次迭代返回的是10,a还没赋值,第二次使用send(20),那么就修改了a的值,a=20。
第一次调用时必须先next()或send(None),负责会报错,send之所以为None是因为没有上一个yield。
>>> def gen():
... print "a"
... x=yield 'abc'
... print "b","x=",x
... y=5+(yield x)
... print "c","y=",y
...
>>> p = gen()
>>> p
<generator object gen at 0x10107d9b0>
>>> p.next()
a
'abc'
>>> p.send(20)
b x= 20
20
>>> p.send(10)
c y= 15
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
使用isgeneratorfunction判断generator函数
>>>from inspect import isgeneratorfunction
>>> isgeneratorfunction(fab)
True
注意区分 fab和fab(),fab是generator function,fab(2)是generator 好比类的定义和类的实例
类的定义和类的实例
>>>import types
>>> isinstance(fab, types.GeneratorType)
False
>>> isinstance(fab(5), types.GeneratorType)
True
fab是无法迭代的,fab(2)是可迭代的
>>>from collections import Iterable
>>> isinstance(fab, Iterable)
False
>>> isinstance(fab(5), Iterable)
True
return的作用:在generator function中,如果没有return,则默认执行至函数完毕,如果在执行过程中return则直接抛出StopIteration终止迭代
实例:读取一个大的文件
def read_file(fpath):
BLOCK_SIZE = 1024
with open(fpath, 'rb') as f:
while True:
block = f.read(BLOCK_SIZE)
if block:
yield block
else:
return
结论: