• 参悟yield 和yield from (加精)


     很久没有复习协程知识了,翻看文档感觉像个小学生一样,参悟了几个小时总算是透了,大佬们莫要见笑。

    yield用法:

      next(itr)相当于 itr.send(None),两种方式都会触发生成器运行

      下面 代码 # 1处 运行至 yield index 的第一次断点 , #2  对jump进行赋值 并运行至第二个断点处 ...... 

    def jumping_range(N):
        index = 0
        while index < N:
            # 通过send()发送的信息将赋值给jump
            jump = yield index
            # print("jump", jump)
            if jump is None:
                jump = 1
            index += jump
    
    if __name__ == '__main__':
        itr = jumping_range(5)
        print(next(itr))     # 1
        print(itr.send(2))    # 2
        print(next(itr))     # 3
        print(itr.send(-1))  #4

     如果想要完成所有的赋值取值, 最后一次next()会触发 StopIteration的错误 可以加try捕捉 

    例如:

    if __name__ == '__main__':
        itr = jumping_range(5)
        print(next(itr))
        print(itr.send(2))
        print(next(itr))
        print(itr.send(-1))
        print(next(itr))
        print(next(itr))
        try:
            print(next(itr))
        except StopIteration as e:
            print(e.value)

    yield from的用法

    不多说直接代码比较 ,下面一个例子

      用yield实现

    # 字符串
    astr='ABC'
    # 列表
    alist=[1,2,3]
    # 字典
    adict={"name":"wangbm","age":18}
    # 生成器
    agen=(i for i in range(4,8))
    
    def gen(*args, **kw):
        print(args)
        for item in args:
            for i in item:
                yield i
    
    new_list=gen(astr, alist, adict, agen)
    print(next(new_list))
    #print(type(new_list))
    #['A', 'B', 'C', 1, 2, 3, 'name', 'age', 4, 5, 6, 7]

      使用yield from 

    # 字符串
    astr='ABC'
    # 列表
    alist=[1,2,3]
    # 字典
    adict={"name":"wangbm","age":18}
    # 生成器
    agen=(i for i in range(4,8))
    
    def gen(*args, **kw):
        for item in args:
            yield from item
    
    new_list=gen(astr, alist, adict, agen)
    print(list(new_list))
    # ['A', 'B', 'C', 1, 2, 3, 'name', 'age', 4, 5, 6, 7]

    由上面两种方式对比,可以看出,yield from后面加上可迭代对象,他可以把可迭代对象里的每个元素一个一个的yield出来,对比yield来说代码更加简洁,结构更加清晰。

    yield from 的深层次应用:

      首先得知道一个概念:

      

    1、调用方:调用委派生成器的客户端(调用方)代码
    2、委托生成器:包含yield from表达式的生成器函数
    3、子生成器:yield from后面加的生成器函数
    

      

    # 子生成器
    def average_gen():
        total = 0
        count = 0
        average = 0
        while True:
            new_num = yield average
            count += 1
            total += new_num
            average = total/count
    
    # 委托生成器
    def proxy_gen():
        while True:
            yield from average_gen()
    
    # 调用方
    def main():
        calc_average = proxy_gen()
        next(calc_average)            # 预激下生成器
        print(calc_average.send(10))  # 打印:10.0
        print(calc_average.send(20))  # 打印:15.0
        print(calc_average.send(30))  # 打印:20.0
    
    if __name__ == '__main__':
        main()

    委托生成器的作用: 在调用方和子生成器之间行成双向通道

    双向通道的含义:调用方可以通过send()直接发送消息给子生成器,而子生成器yield的值,也是直接返回给调用方。

    注意:

    你可能会经常看到有些代码,还可以在yield from前面看到可以赋值。这是什么用法?

    你可能会以为,子生成器yield回来的值,被委托生成器给拦截了。你可以亲自写个demo运行试验一下,并不是你想的那样。
    因为我们之前说了,委托生成器,只起一个桥梁作用,它建立的是一个双向通道,它并没有权利也没有办法,对子生成器yield回来的内容做拦截。

    例子:

    # 子生成器
    def average_gen():
        total = 0
        count = 0
        average = 0
        while True:
            new_num = yield average
            if new_num is None:
                break
            count += 1
            total += new_num
            average = total/count
    
        # 每一次return,都意味着当前协程结束。
        return total,count,average
    
    # 委托生成器
    def proxy_gen():
        while True:
            # 只有子生成器要结束(return)了,yield from左边的变量才会被赋值,后面的代码才会执行。
            total, count, average = yield from average_gen()
            print("计算完毕!!
    总共传入 {} 个数值, 总和:{},平均数:{}".format(count, total, average))
    
    # 调用方
    def main():
        calc_average = proxy_gen()
        next(calc_average)            # 预激协程
        print(calc_average.send(10))  # 打印:10.0
        print(calc_average.send(20))  # 打印:15.0
        print(calc_average.send(30))  # 打印:20.0
        calc_average.send(None)      # 结束协程
        # 如果此处再调用calc_average.send(10),由于上一协程已经结束,将重开一协程
    
    if __name__ == '__main__':
        main()

    结果:

    10.0
    15.0
    20.0
    计算完毕!!
    总共传入 3 个数值, 总和:60,平均数:20.0

     

    浅层次谈论yield from的作用,它可以帮我们处理异常

  • 相关阅读:
    词法分析
    HTTP学习笔记
    Servlet入门
    UDP与TCP的区别
    C语言实现血型查询系统
    Mysql的索引、回表查询及覆盖索引浅析
    ReentranLock浅析
    CAS是个什么鬼?
    synchronize和volatile 小知识点总结
    写一个简单的阻塞队列
  • 原文地址:https://www.cnblogs.com/cjj-zyj/p/10407154.html
Copyright © 2020-2023  润新知