Python yield使用理解
通常在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
结论:
yield是一个类似return的关键字,只是这个函数返回的生成器
当你调用这个函数的时候,函数内部并不会立即执行,只会返回一个生成器对象
当你for进行迭代的时候,函数中的代码才会执行