yield from

一、yield
关于yield详细可参考我这篇文章

下面是一个带yield的生成器:

def gen_yield():
    while True:
        recv = yield
        do something with recv

现在我们不单独使用gen_yield这生成器,而是通过另一个携程outer_yield完成必要的验证、设置等再去使用它

def outer_yield():
    chiled_gen = gen_yield()
    chiled_gen.send(None) #准备上子携程
    
    do something just like setting or you want #在这儿做一些设置或者验证工作
    
    while True:
        recv = yield
        chiled_gen.send(recv) #把上面接收到的消息,发送到子携程处理

以上可知,yield 只允许协程与外界直接交互而不能使得子协程与外界直接交互。而且,我们这里还没有考虑子协程抛出异常,返回等情况。

yield form 应运而生来解决这个问题:

def gen_yield():
    while True:
        recv = yield
        do something with recv

def new_outer():

    do something you want
    yield from gen_yield()         #1 yield from 后必须跟generator object

no = new_outer()
no.send(None)  #2启动生成器

这儿我们可以理解为#1 处yield from 把工作下发(委托)给了gen_yield()子生成器
而当#2出外界开始send值与outer交互时,outer直接通过yield from把值发送给子生成器gen_yield执行
这样就达到了外界直接与子生成器直接交互的功能。

总结:

  1. 所有new_outer的实例作为调用者把值send过去,都等于直接发送给了子迭代器gen_yield,而子迭代器yield回来的值也登月直接给了调用者。意思就是nwe_outer委托gen_yield工作。
  2. 当new_outer实例调用send方法,就等于gen_yield子迭代器同步调用send方法,把值send到子迭代器内部yield。
  3. 当子迭代器gen_yield内部捕获到StopIteration时,子迭代器内部停止执行,开始执行委托者new_outer.
  4. 委托生成器有一个 throw() 方法可发送任何除了 GeneratorExit 的异常都将被直接发送给子迭代器 。同理,如果这次调用产生了 StopIteration 异常,那么委托生成器 new_outer 将被恢复执行
  5. 如果向委托生成器发送了 GeneratorExit 异常或者调用委托生成器的 close() 方法,那么首先子迭代器 b 的 close() 方法就会被调用(如果有的话)。随后,委托生成器就会引发 GeneratorExit 异常,从而退出
  6. 如果迭代器 gen_yield中有 return value ,那么当 子迭代器 执行 return 返回时,等价于 raise StopIteration(value)(迭代器总是通过引发StopIteration 异常停止),之后该 value 将成为 yield from 表达式值。
  7. 在任意运行时刻,如果迭代器gen_yield抛出了未捕获的异常,那么该异常将会被传播给委托生成器new_outer
带return值的列子
>>> def inner_gen():
...    sum_all = 0
...    while True:
...        x = yield
...        print('recv : {0}'.format(x))
...        if x is None:
...            return sum_all
...        sum_all = sum_all + x
...
>>> def outer():
...    while True:
...        sum_all = yield from inner_gen()
...        print('sum_all is: {0}'.format(sum_all))
...        print('outer 执行完毕')
...

>>> out = outer()
>>> out.send(None)  # 准备好协程
>>> for i in range(3):
...     out.send(i)    
...
recv: 0
recv: 1
recv: 2
>>> out.send(None)  # 停止子协程
recv : None
sum_all is: 3
outer 执行完毕
>>> for i in range(5):
...     out.send(i)
...
recv: 0
recv: 1
recv: 2
recv: 3
recv: 4
>>> out.send(None)
recv: None
sum_all is: 10
outer 执行完毕

>>> out.throw(TypeError)  # TypeError 来自 inner_gen 函数
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in outer
  File "<stdin>", line 4, in inner_gen
TypeError

使用 send() 方法发送的值将被直接发送字生成器 inner_gen() 中了,throw() 同理。当子迭代器返回时,return 后的值成为 yield from 表达式的值。

yield from 允许外界与子协程直接交互,这样就允许代码重构:把一部分包含 yield 的代码放到另外的函数,再使用 yield from 调用该迭代器。这样就实现委托工作了

原文地址:https://www.cnblogs.com/shiqi17/p/9444675.html