- 生成器函数:使用常规的def语句进行编写,但是使用yield语句一次返回一个结果,在每次结果产生之间挂起和恢复他们的状态。
- 生成器表达式:类似列表推导,但是,它们返回按需产生结果的一个对象,而不是创建一个结果列表。
def gensquares(N): for i in range(N): yield i**2 for i in gensquares(5): print(i,end=':') #0:1:4:9:16: x=gensquares(r) next(x) #0 next(x) #1 next(x) #4
返回的生成器对象有一个__next__方法。
- 生成器对大型程序而言,在内存使用和性能方面都更好。它们允许函数避免预先做好所有的工作,在结果的列表很大或者在处理每一个结果都需要很多时间时,生成器将产生一系列值的时间分散到每一次的循环迭代中去。
- 生成器的send方法生成一系列结果的下一个元素,值可以通过调用G.send(value)发送给一个生成器G。之后恢复生成器代码的执行,并且生成器中的yield表达式返回了发送给send函数的值。如果提前调用了正常的G.__next__方法,yield则返回None。
def gen(): for i in range(10): x=yield i print(x) G=gen() next(G) #0 G.send(77) #77 1 G.send(88) #88 2 next(G) #None 3
- 生成器表达式就像一般的列表推导一样,而且也支持所有列表推导的语法(包括if过滤器和循环嵌套),但它们是包括在圆括号中而不是方括号里的(跟元组一样,它们的圆括号通常是可选的)。
- 生成器表达式不是在内存中构建结果,而是返回一个生成器对象——一个自动被创建的可迭代对象。这个可迭代对象会支持迭代协议,并在任意的迭代语境中产生一个结果列表。可迭代对象在激活时也持有生成器的状态。可以出现在任何的迭代上下文中。如果生产期表达式包在其它的括号之内,比如在函数调用之中,生成器自身的括号就不是必须的。生成器表达式也是一种对内存空间的优化,他们将生成结果拆分成更小的时间片:他们会一部分一部分产生结果,而不是让调用者再一次调用中等待整个集合中被创建出来。
G=(x**2 for x in range(4)) iter(G) is G #True next(G) #0 next(G) #1 for num in (x**2 for x in range(4)): print('%s,%s'%(num,num/2.0) #0,0.0 #1,0.5 #4,2.0 #9,4.5 sum(x**2 for x in range(4)) #14,不需要括号
- 生成器函数和表达式自身都是迭代器,因此只支持一次活跃迭代,一旦任意迭代器运行结束,所有迭代器都将用尽——我们必须产生一个新的生成器一边重新开始。
- yield form扩展:
def both(N): yield from range(N) yield from (x**2 for x in range(N)) list(both(5)) #[0,1,2,3,4,0,1,4,9,16]