• 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 调用该迭代器。这样就实现委托工作了

  • 相关阅读:
    Java_swing控件实例
    java_泛型 TreeSet 判断hashcode/length(升序排列)
    java_泛型(设置通配符下限)
    java_泛型(构造器)部分实例
    子类可以继承的同时实现接口
    继承类的线程写法
    匿名内部类的线程写法
    接口作为参数并用参数变量可以调用接口中的方法------------------需要多练习
    类可以作为参数类型,参数的变量还能调用作为参数类型的方法--------------需要多练习
    接口和类
  • 原文地址:https://www.cnblogs.com/shiqi17/p/9444675.html
Copyright © 2020-2023  润新知