• 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
  • 相关阅读:
    科研热点|多所高校公布2022国家杰青、优青基金获得者(附名单及查询方法)~
    Kotlin Flow啊,你将流向何方?
    Netty 网络传输TCP粘包拆包
    简介:Asp.Net Core进阶高级编程教程
    centos7 安装pg13
    网口工业相机之丢包问题排查
    lock、tryLock、lockInterruptibly有什么区别?
    Android14之input高级调试技巧(一百八十八)
    如何实现MySQL的增删改查
    CSDN第11期周赛题解
  • 原文地址:https://blog.csdn.net/mitudeniu/article/details/126494170