生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果,在每个结果之间挂起和继续它们的状态,来自动实现迭代协议。也就是说,yield是一个语法糖,内部实现支持了迭代器协议,同时yield内部是一个状态机,维护着挂起和继续的状态。
[TOC]
简单使用
我们先定义了一个生成器函数,函数返回一个生成器对象,然后就可以通过for语句或者next()进行迭代访问.这里我们直接让迭代器列表化打印.def Lrange(n):
i = 0
while i < n :
yield i
i += 1
lrange=Lrange(3)
list_l = list(lrange)
print(lrange)
print(list_l)
>>>
<generator object Lrange at 0x0EAA3D80>
[0, 1, 2]
其实,生成器函数返回生成器的迭代器。 “生成器的迭代器”这个术语通常被称作”生成器”。要注意的是生成器就是一类特殊的迭代器。作为一个迭代器,生成器必须要定义一些方法,其中一个就是next()。如同迭代器一样,我们可以使用next()函数来获取下一个值。
执行过程
- 当调用生成器函数的时候,函数只是返回了一个生成器对象,并没有 执行。
- 当next()方法第一次被调用的时候,生成器函数才开始执行,执行到yield语句处停止
- next()方法的返回值就是yield语句处的参数(yielded value)
- 当继续调用next()方法的时候,函数将接着上一次停止的yield语句处继续执行,并到下一个yield处停止;如果后面没有yield就抛出StopIteration异常
生成器推导式
说到推导式,你可能首先会想到列表推导式,其实两者还是真的很相似.
举个栗子,哈哈
现在的需求是:我们要生成1到20的奇数一个列表.[i for i in range(1,20) if i%2]
那么将这个列表元素不直接都存到列表里,而是以迭代器的方式,每次取一个.这样就是生成器了,是不是还是蛮好理解的.那么怎么将这个列表推导式改成生成器推导式呢?很简单,把方括号改成小括号就可以了,这时候我们返回的就不是一个列表,而是一个迭代器.ge = (i for i in range(1,20) if i%2)
ge2=[i for i in range(1,20) if i%2]
print(ge)
print(ge.__next__())
print(ge2)
>>>
<generator object <genexpr> at 0x0E053D80>
1
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
这样的对比是不是直观多了.
那生成器是不是完全就是迭代器,当然不是,他还有其他的方法,
send()方法
Python 2.5中,yield语句变成了yield表达式,也就是说yield可以有一个值,而这个值就是send()方法的参数,所以send(None)和next()是等效的。同样,next()和send()的返回值都是yield语句处的参数(yielded value)
关于send()方法需要注意的是:
调用send传入非None值前,生成器必须处于挂起状态,否则将抛出异常。也就是说,第一次调用时,要用next()语句或send(None),因为没有yield语句来接收这个值。
close()方法
这个方法用于关闭生成器,对关闭的生成器后再次调用next或send将抛出StopIteration异常。