• python学习05-协程


    1、生成器与协程的关系

    从语法上看,协程与生成器类似,都是定义体中包含yield关键字的函数。普通函数将return改为yield就变成生成器函数,生成器函数返回的结果就是生成器
    但在协程中,yield通常出现在表达式的右边(如,x = yield),可以产出值,也可以不产出,如果yield关键字后面没有表达式,生成器就产出None
    协程可能会从调用方接收数据,不过调用方把数据提供给协程使用的是.send(datum)方法,而不是next()函数,通常调用方会把值推送给协程
    不管数据如何流动,yield都是一种流程控制工具,可以实现协作式多任务,协程可以把控制器让步给中心调度程序,从而激活其他的协程

    #协程使用生成器函数定义:定义体中有yield关键字
    #如下为一个简单的协程
    def simple_coroutine():
        print('start')
        x = yield
        print('x:',x)
        print('finish')
    my_coro = simple_coroutine()
    next(my_coro)#第一次使用,首先需要调用next()方法启动生成器,如果生成器没有启动,没有yield语句处暂停,就无法发送数据
    
    my_coro.send(6)
    #生成器使用yield时,只用于返回生成的值,而不会接收值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    #协程使用yield时,除了可以返回值,还可以接收值。生成器和协程在定义上很相近,在具体的实现上也是类似的

    2、协程的状态

    协程可以身处于4个状态中的一个,当前状态可以使用inspect.getgeneratorstate(…)函数确定,该函数会返回下述字符串中的一个。
    'GEN_CREATED’等待开始执行
    'GEN_RUNNING’解释器正在执行
    'GEN_SUSPENDED’在yield处暂停
    'GEN_CLOSED’执行结束

    def simple_coro(a):
        print('start:a = ',a)
        #返回a的值,获取send()传入的值
        b = yield a
        print('received:b = ',b)
        c = yield a + b
        print('received:c = ',c)
    #获得协程对象
    my_coro = simple_coro(1)
    from inspect import getgeneratorstate
    print(getgeneratorstate(my_coro))
    #向前执行协程到第一个yield表达式,打印start:a = 14消息后,产出a的值,并且暂停,等待为b赋值
    next(my_coro)
    print(getgeneratorstate(my_coro))
    #把数字2发送给暂停的协程,计算yield表达式,得到2,把那个数绑定给b
    #打印received:b = 2消息,产出a + b 的值3,然后协程暂停,等待为c赋值
    print(my_coro.send(2))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    #使用协程计算移动平均值,原始闭包实现

    def make_avg():
        count = 0
        total = 0
        def avg(new_value):
            nonlocal count,total
            count += 1
            total += new_value
            return total / count
        return avg
    
    avg = make_avg()
    
    print(avg(5))
    print(avg(6))
    
    def make_avg():
        total = 0
        count = 0
        avg = None
        while True:#无限循环表明,只要调用方法不断把值发给这个协程,它就会一直接收,然后生成结果。
            term = yield avg
            total += term
            count += 1
            avg = total / count
    
    avg_coro = make_avg()
    next(avg_coro)
    print(avg_coro.send(5))
    print(avg_coro.send(6))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    #调用next(coro_avg)函数后,协程会向前执行到yield表达式,产出average变量的初始值None,因此不会出现在控制台中
    #此时,协程在yield表达式处暂停,等到调用方发送值,激活协程,把发送的值赋给term,更新total、count、average三个变量
    #的值,开始while的下一次迭代,产出average变量的值,等待下一次为term变量赋值

    #使用装饰器预激活协程

    from functools import wraps
    
    def corotine(func):
        @wraps(func)
        def wrapper(*args,**kwargs):
            gen = func(*args,**kwargs)
            next(gen)
            return gen
        return wrapper
    
    @corotine
    def make_avg():
        total = 0
        count = 0
        avg = None
        while True:#无限循环表明,只要调用方法不断把值发给这个协程,它就会一直接收,然后生成结果。
            term = yield avg
            total += term
            count += 1
            avg = total / count
    
    avg_coro = make_avg()
    print(avg_coro.send(5))
    print(avg_coro.send(6))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
  • 相关阅读:
    跟着官方帮助文档学ICEM网格划分(附视频教程)
    GEE开发之Landsat8计算NDWI和数据分析
    关于Synchronized你了解多少?
    Golang开发--select
    input输入多行文本:删除“首先 其次 此外 总的来说”
    倒计时第3天!Google Summer of Code报名即将截止!(Casbin社区还有空缺名额)
    nuxt页面访问速度优化
    为什么 Python、Ruby 等语言弃用了自增运算符?
    SpringCloud
    图论-最短路径问题
  • 原文地址:https://blog.csdn.net/mitudeniu/article/details/126494170