1.生成器简述
函数使用关键字 yield 可以定义一个生成器对象。生成器是一个函数,他生成一个值的序列,以便在迭代中使用。
如下代码我们先编写一个生成器
def count(n):
print('before yield n %' ,n)
while n > 0:
yield n
print('after yield %', n)
n -= 1
return
count(8)
在末尾调用count(8),按照最开始的认识是肯定会打印 “before yield n % 8”,但是在调试环境上什么也没打印!这是为什么呢?
这其中的原因就在yield,调用count函数,代码并不会执行,但是他会返回一个生成器对象。紧接着,该生成器对象就会在
“__next__()”被调用时执行函数。
如下,我没对代码做一下简单修改
def count(n):
print('before yield n %' ,n)
while n > 0:
yield n
print('after yield %', n)
n -= 1
return
c = count(8)
i = 0
for i in range(8): #在for循环中反复调用__next__()方法
i += 1
c.__next__()
其打印结果如下:
#将上述代码中的n改为9可以观察到生成器函数结束的打印
before yield n % 8
after yield % 8
after yield % 7
after yield % 6
after yield % 5
after yield % 4
after yield % 3
after yield % 2
由以上打印结果我们就可以准确的推断出,调用__next__()方法时,生成器函数开始执行语句,直到遇到yield语句为止。
yield语句在函数执行停止的地方生产一个结果,直到再次调用__next__()方法,然后继续执行yield之后的语句。
一般情况下不会再生成器上直接调用__next__()方法,而是在一些for循环或者sum等消耗语句序列中使用生成器。
生成器函数完成的标志是返回或者引发StopIteration异常,这标志着迭代的结束。如果生成器在完成时返回None之外的值,都是不合法的。
2.yield表达式
以yield语句作为表达式的函数称之为协程。
如下代码,就构造了一个协程
def receiver():
print("Ready to receive")
while True:
n = (yield)
print("Got %s" % n)
r = receiver()
r.__next__()
r.send('haha')
其中一开始调用__next__()是必不可少的,这样的协程才能执行第一个yield表达式之前的语句。这时协程会挂起,等待相关生成器的对象r的seng()方法给他发送一个值。传递给send()的值由协程中的yield表达式返回。接收到值后,协程语句就会向下执行,直到遇到下一条yield语句。当然协程一般会不断的执行下去,除非被显式的关闭或者自己退出。可以使用方法close().
在一开始调用__next__()这个动作容易被忽视可以整一个装饰器让其自动完成,代码如下
def cor(func):
def start(*args, **kwargs):
g = func(*args, **kwargs)
g.__next__()
return g
return start
@cor
def receiver():
print("Ready to receive")
while True:
n = (yield)
print("Got %s" % n)
r = receiver()
r.send('haha')
r.close() #最后显式的关闭这个流
#最后打印出来的结果为
Ready to receive
Got haha
总结
- 本节我们学习了yield的基本概念和简单用法
- 第二部分学习了yield更深一层的用法,当然这只是最基础的,教材上还给出了生成器与协程在解决系统、网络和分布式计算方面的实例,但是鉴于目前学习初级阶段故不做深究,待后面学完基础知识后再做补充。