通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,
不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。
生成器(generator)就是迭代器(iterator)的一种,以更优雅的方式实现的iterator,而且完全可以像使用iterator一样使用generator。当然除了定义,定义一个iterator,你需要分别实现_ _ iter _ _ ()方法和 _ _ next_ _()方法,但generator只需要一个小小的yield
iterator通过__next__()方法实现了每次调用,返回一个单一值的功能.而yield就是实现generator的__next__()方法的关键!

第一次调用next()方法时,函数似乎执行到yield 1,就暂停了.然后再次调用next()时,函数从yield 1之后开始执行的,并再次暂停.第三次调用next(),从第二次暂停的地方开始执行.第四次,抛出StopIteration异常.
将yield理解成一个中断服务子程序的断点,没错,是中断服务子程序的断点.我们每次对一个generator对象调用next()时,函数内部代码执行到”断点”yield,然后返回这一部分的结果,并保存上下文环境,”中断”返回.
generator 其实有2种调用方法(恢复执行),即通过send(value)方法将value作为yield表达式的当前值。你可以用该值再对其他变量进行赋值。当我们调用send(value)方法时,generator正由于yield的缘故被暂停了。此时,send(value)方法传入的值作为yield表达式的值,函数中又将该值赋给了变量s,然后print函数打印s,循环再遇到yield,暂停返回。
def foo(value=None):
while 1:
value = (yield value)
print("the value is{0}".format(value))
if value:
value += 1
>>> g = foo(1)
>>> next(g)
1
>>> g.send(2)
the value is2
3
>>> g.send(4)
the value is4
5
>>> next(g)
the value isNone
>>>
第一次调用next()方法,执行到yield value表达式,保存上下文环境暂停返回1.第二次调用send(value)方法,从value = yield开始,打印,再次遇到yield value暂停返回.后续的调用send(value)或next()都不外如是.
g = (i for i in range(10))>>> type(g)
<class 'generator'>
>>> g.gi_frame.f_lasti
-1
>>> g.send(None)
0
>>> g.gi_frame.f_lasti
8
>>> len(g.gi_code.co_code)
18
>>>
堆栈帧有个 “last instruction”(FIXME: translate this or not?) 指针,指向最近执行的那条指令。刚开始的时候 last instruction 指针是 -1,意味着生成器尚未开始:
当我们调用 send 时,生成器达到第一个 yield 处然后暂停执行。send 的返回值是 0,这是因为 g把 0 传给了 yield 表达式
现在生成器的指令指针(instruction pointer)向前移动了 8 个字节码,这些是编译好的18字节的 Python 代码的一部分
生成器可以在任何时候被任何函数恢复执行,因为它的堆栈帧实际上不在堆栈上——它在堆(内存)上。生成器在调用调用层次结构中的位置不是固定的,它不需要遵循常规函数执行时遵循的先进后出顺序。生成器被是被解放了的,它像云一样浮动。
总结:
可迭代对象(Iterable)是实现了__iter__()方法的对象,通过调用iter()方法可以获得一个迭代器(Iterator)
迭代器(Iterator)是实现了__iter__()和__next__()的对象
for ... in ...的迭代,实际是将可迭代对象转换成迭代器,再重复调用next()方法实现的
生成器(generator)是一个特殊的迭代器,它的实现更简单优雅.
yield是生成器实现__next__()方法的关键.它作为生成器执行的暂停恢复点,可以对yield表达式进行赋值,也可以将yield表达式的值返回.
文章参考来源:http://kissg.me/2016/04/09/python-generator-yield/
网友评论