首先,先理解一个概念, 把yield 当作是一种流程控制工具.
1 . yield 将在产出值后 停止. 例如: yield 1 . 当 1 产出后,此生成器将停止运行,将控制权交给调用者.
yield 同时可以获取值, 如果调用方 send();
2. yield from 如果在函数中, 则此函数也是一个生成器, 与 yield 一样, 终止是也会抛出StopIteration
yield from 何时启动 ,运行至哪?
由下面第4个例子可知 .yieldfrom 是一个生成器,所以 next() 即启动, 运行到yield from 后面的子生成器中的yield 暂停;
yield from 何时停止 ?
由下面第4个例子可知 . 当子生成器抛出StopIteration时, yield from 将获取返回值 . 并由于 yield from 自身是一个生成器.
所以再次抛出StopIteration;
3. yield from 实现代码: yield from 实现
#自己实现一个 itertools.chain 函数
def iter_func(*args):
for it in args:
for item in it:
yield item
g = iter_func('abc','123') #2个可迭代对象(str)
print(list(g))
#输出:['a', 'b', 'c', '1', '2', '3']
使用 yield from 修改一下 , 可以看到 yield from 可以代替 for . 用于迭代
def iter_func2(*args):
for it in args:
yield from it
g = iter_func2('abc','123')
print(list(g))
#输出 :['a', 'b', 'c', '1', '2', '3']
#与上个例子是一样的.
获取协程返回的值 , 返回的值在异常中:
#让协程返回一个值
Result = namedtuple('Result', "count avg")
def avg():
total = 0
count = 0
average = 0
while True:
print('enter avg while block')
recv = yield average
print('after yield , recvied: ' , recv)
if recv == None:
break
total += recv
count +=1
average = total / count
return Result(count,average)
a = avg()
next(a) #激活, 运行到yield 产出值后停止
a.send(10)
a.send(30)
try:
a.send(None)
except StopIteration as ex:
print("获取结果: " , ex.value)
yield from 将捕获异常, 并获取返回的值 , 修改上面捕获异常的代码 , avg函数不变 . 注意yield from 只能写在函数内:
如果有unix - c 编程基础的可以看一下 :
yield from 相当于是一个 socketpair / pipe的全双工管道 , 父进程(调用方) 写入数据( send ), 子进程( yield 子生成器) 从管道中读 取数据( yield 获取数据),
最终,子进程把数据写入管道由父进程获取 ( yield from 返回值)
在这个例子中, 暂且称avg 是一个子生成器 ,由 yield from 调用;
from inspect import getgeneratorstate
def yield_from_func():
'''
将控制权移交到 avg 中. 而avg 中的yield 后,将控制权移交给调用方,即__main__.
请注意 yield from 跟 yield 一样, 都不是阻塞性的
yield from 就像电话线一样, 能让调用方 与 avg() 子生成器 进行通话
'''
ret = yield from avg()
print("ret : " , ret) # 这行代码将在 avg() 抛出异常后运行
a = yield_from_func()
print("yield from func 是一个函数:",yield_from_func)
print("a 是一个生成器 :",a)
print("type(a) :" , type(a))
next(a) #激活 , 将运行到 avg 中的 yield
ret = a.send(10) # 传递给avg 中的yield , yield 获取 10 , yield from 就像一个管道一样
print("result from yield :", ret) # 由 avg 中的yield 产出
print(getgeneratorstate(a)) #获取生成器状态
a.send(None) # avg 中断, yield from 捕获异常, 获取返回值. 至此, yield from 结束返回
上面的代码将抛出 StopIteration , 此异常是 yield from 抛出的, 因为yield from 的函数也是一个生成器, 一旦结束就抛StopIteration
yield from 就跟管道一样, 能让调用方与一个子生成器相互传递数据 , 一个send , 一个 yield .
yield from 作为中间组件存在,转移控制权给子生成器, 直到子生成器抛出异常, yield from 将捕获异常,
同时恢复运行.