在python中,带yield的函数称为生成器(generator),python对协程的支持也是通过生成器实现的。
yield
首先将yield当成一个断点标记,中断,return 。当程序执行到yield处,返回yield后边的变量,中断。其他程序获取这个变量,调用生成器的next()函数,程序又回到生成器,接着向下执行。
一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(next(g))
执行结果
starting...
4
********************
res: None
4
1首先foo函数含有yield,是一个生成器,g = foo(),并不执行foo()函数,而是返回一个迭代器对象赋值给g。
2 调用迭代器对象g的next()方法,foo函数开始执行,打印"starting…",进入while循环,执行到yield,中断,return后边的变量4,程序跳转回到print(next(g))。即print(yield返回的变量4)。
3 顺序执行print("*"*20)。
4 再次调用迭代器对象g的next()方法,程序跳转到上次next结束的地方,即res赋值操作,因为变量4被return出去了,所以res赋值为空。执行print(“res:”,res),即打印res: None,继续循环到yield,再次中断,return 后边的变量4,然后跳转到print(next(g)),即打印4。
生成器有一个重要的方法next(),调用next(),程序从上次next()停止的地方开始执行,直至遇到yield,返回后面的变量,此步停止。
send例子
send(value)
迭代器对象的send方法,发送一个值value给生成器,并返回下一个yield的值(也就行调用next()方法)。
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(g.send(7))
结果
starting...
4
********************
res: 7
4
最后一步:g.send(7),回到上次yield结束的地方,即res赋值操作,即把send的值7赋值给res,继续执行,即打印res,然后执行到yield,返回yield后面的变量4,中断跳出。回到print(g.send(7)),即打印pirnt(4).
使用生成器可以节省内存空间,比如for i in range(1000),则会生成一个list存储1000个元素,而for i in xrange(1000),xrange(1000)就是一个生成器,每次yield 一个元素。
读取文本,如果直接读取,会将文本内容全部加载到内存中,占用大量内存,而生成器则每次yield固定大小BLOCK_SIZE 的文本块到内存。
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
send函数和for循环都会自动调用next()函数。当函数执行结束时,generator 自动抛出 StopIteration 异常,表示迭代完成。在 for 循环里,无需处理 StopIteration 异常,循环会正常结束。
Python对协程的支持是通过generator实现的。
在generator中,我们不但可以通过for循环来迭代,还可以不断调用next()函数获取由yield语句返回的下一个值。但是Python的yield不但可以返回一个值,它还可以接收调用者发出的参数。
生产者–消费者模型
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
r = '200 OK'
def produce(c):
c.send(None) #启动生成器 想当与next(c)
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()
c = consumer()
produce(c)
1 c = consumer(),返回一个迭代器对象。并不执行consumer,在调用next方法才会执行。
2 produce(c )中,c.send(None) 启动生成器。produce一旦生产了东西,通过 c.send(n)切换到consumer,consumer通过yield拿到东西,经过处理后,有经过下一个yield返回。pruduce拿到处理的结果,继续生产下一个东西。produce决定不生产,通过c.close()关闭consumer,整个过程结束.
链接: https://blog.csdn.net/mieleizhi0522/article/details/82142856
链接: https://www.runoob.com/w3cnote/python-yield-used-analysis.html
链接: https://www.liaoxuefeng.com/wiki/1016959663602400/1017968846697824#0
来源:CSDN
作者:sgcwddhr
链接:https://blog.csdn.net/sgcwudi/article/details/103718025