美文网首页
Python-学习之路-16 协程

Python-学习之路-16 协程

作者: 末世狂人 | 来源:发表于2019-03-11 22:54 被阅读0次

    协程

    迭代器

    • 可迭代(iterable):可直接作用for循环的变量
    • 迭代器(iterator):不经可以作用域for循环,还可以倍next调用
    • list是一个典型的可迭代对象,但不是迭代器
    • 通过 isinstance判断
    • iterable和iterator是可以互相转换的
      • 通过iter函数
    #isinstance案例
    
    from collections import Iterable
    from collections import Iterator
    
    ll = [1,2,3,4,5,6]
    #判断是否可迭代
    print(isinstance(ll,Iterable))
    #判断是否是迭代器
    print(isinstance(ll,Iterator))
    
    #转换为迭代器
    ll_iter = iter(ll)
    #判断是否可迭代
    print(isinstance(ll_iter,Iterable))
    #判断是否是迭代器
    print(isinstance(ll_iter,Iterator))
    
    True
    False
    True
    True
    

    生成器

    • generator:一边循环一边计算下一个元素的机制\算法
    • 需满足三个条件:
      • 每次调用都能够生产出for循环需要的下一个元素
      • 如果达到最后一个后,爆出stopIteration异常
      • 可以被next调用

    如果生成一个生成器

    • 直接使用
    • 包含yield,则这个函数就叫生成器
    # 直接使用生成器
    # 放在中括号中是列表生成器
    l = [x*x for x in range(5)]
    # 放在小括号中就是生成器
    g = (x*x for x in range(5))
    
    print(type(l))
    print(type(g))
    
    <class 'list'>
    <class 'generator'>
    
    # 函数案例
    def odd():
        print("step 1")
        yield 1
        print("step 2")
        yield 2
        
    g = odd()
    one  = next(g)
    print(one)
    two  = next(g)
    print(two)
    
    step 1
    1
    step 2
    2
    
    # for 循环调用生成器
    def fib(max):
        n, a, b = 0, 0, 1
        while n < max:
            yield b
            a ,b = b, a+b
            n +=1
        # 注意,爆出的异常是return返回的值 
        return 'Done'
    
    g = fib(5)
    
    for i in range(6):
        rst = next(g)
        print(rst)
    
    1
    1
    2
    3
    5
    
    
    
    ---------------------------------------------------------------------------
    
    StopIteration                             Traceback (most recent call last)
    
    <ipython-input-3-e8ff877cca38> in <module>
         12 
         13 for i in range(6):
    ---> 14     rst = next(g)
         15     print(rst)
    
    
    StopIteration: Done
    
    ge = fib(10)
    '''
    生成器的典型用法就是在for循环中使用
    '''
    for i in ge:
        print(i)
    
    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    

    参考资料来自:https://blog.csdn.net/andybegin/article/details/77884645

    协程的历史

    • 3.4 引入协成,用yield实现
    • 3.5 引入协成语法
    • 实现协程比较好的包有 asyncio,tornado,gevent
    • 从技术角度讲,协成就是一个可以暂停执行的函数,或者可以把协成理解成生成器
    • 协成的实现
      • yield 返回
      • send 调用

    协程的四个状态

    • 协程可以身处四个状态中的一个。当前状态可以使用inspect.getgeneratorstate(…) 函数确定,该函数会返回下述字符串中的一个:
      1. GEN_CREATED:等待开始执行
      2. GEN_RUNNING:解释器正在执行
      3. GEN_SUSPENED:在yield表达式处暂停
      4. GEN_CLOSED:执行结束
    #协成的案例1
    def simple_coroutine():
        print("-->start")
        x = yield
        print("-->recived",x)
        
    # 主线程
    sc = simple_coroutine()
    print(1111)
    
    # 预激(可以理解为激活协程)
    # 同时可以使用se.send(None) 效果与next(sc)一致
    next(sc)
    
    print(22222)
    #调用协程
    sc.send("wangdana")
    
    1111
    -->start
    22222
    -->recived wangdana
    
    
    
    ---------------------------------------------------------------------------
    
    StopIteration                             Traceback (most recent call last)
    
    <ipython-input-3-9b0f93417b35> in <module>
         15 print(22222)
         16 #调用协程
    ---> 17 sc.send("wangdana")
    
    
    StopIteration: 
    
    # 协成案例2
    def simple_coroutine(a):
        print('-> start') 
        b = yield a 
        print('-> recived', a, b) 
        c = yield a + b 
        print('-> recived', a, b, c)
    # run 
    sc = simple_coroutine(5)
    next(sc) 
    sc.send(6) # 5, 6 
    sc.send(7) # 5, 6, 7
    
    
    -> start
    -> recived 5 6
    -> recived 5 6 7
    
    
    
    ---------------------------------------------------------------------------
    
    StopIteration                             Traceback (most recent call last)
    
    <ipython-input-3-e9591ee2bc46> in <module>
         10 next(sc)
         11 sc.send(6) # 5, 6
    ---> 12 sc.send(7) # 5, 6, 7
    
    
    StopIteration: 
    

    解释:

    1. 调用 next(ag) 函数后,协程会向前执行到 yield 表达式,产出 average 变量的初始值——None。
    2. 此时,协程在 yield 表达式处暂停。
    3. 使用 send() 激活协程,把发送的值赋给 num,并计算出 avg 的值。
    4. 使用 print 打印出 yield 返回的数据。

    终止协程和异常处理

    • 协程中未处理的异常会向上冒泡,传给 next 函数或 send 方法的调用方(即触发协程的对象)。
    • 终止协程的一种方式:发送某个哨符值,让协程退出。内置的 None 和Ellipsis 等常量经常用作哨符值

    显式地把异常发给协程

    • 从 Python 2.5 开始,客户代码可以在生成器对象上调用两个方法,显式地把异常发给协程。

    generator.throw(exc_type[, exc_value[, traceback]])

    • 致使生成器在暂停的 yield 表达式处抛出指定的异常。如果生成器处理了抛出的异常,代码会向前执行到下一个 yield 表达式,而产出的值会成为调用 generator.throw方法得到的返回值。如果生成器没有处理抛出的异常,异常会向上冒泡,传到调用方的上下文中。

    generator.close()

    • 致使生成器在暂停的 yield 表达式处抛出 GeneratorExit 异常。如果生成器没有处理这个异常,或者抛出了 StopIteration 异常(通常是指运行到结尾),调用方不会报错。如果收到 GeneratorExit 异常,生成器一定不能产出值,否则解释器会抛出RuntimeError 异常。生成器抛出的其他异常会向上冒泡,传给调用方。

    yield from获取协程的返回值

    • 为了得到返回值,协程必须正常终止;然后生成器对象会抛出StopIteration 异常,异常对象的 value 属性保存着返回的值。
    • yield from 结构会在内部自动捕获 StopIteration 异常==。对 yield from 结构来说,解释器不仅会捕获 StopIteration 异常,还会把value 属性的值变成 yield from 表达式的值。

    yield from基本用法

    • 在生成器 gen 中使用 yield from subgen() 时, subgen 会获得控制权,把产出的值传给 gen 的调用方,即调用方可以直接控制 subgen。与此同时, gen 会阻塞,等待 subgen 终止
    # yield from 案例
    def gen():
        for c in 'AB':
            yield c
    
    print(list(gen()))
    
    def gen_new():
        yield from 'AB'
    
    print(list(gen_new()))
    
    ['A', 'B']
    ['A', 'B']
    

    yield from高级用法

    • yield from 的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样二者可以直接发送和产出值,还可以直接传入异常,而不用在位于中间的协程中添加大量处理异常的样板代码

    yield from 专门的术语

    委派生成器:包含 yield from 表达式的生成器函数。
    子生成器:从 yield from 中 部分获取的生成器。
    
    from collections import namedtuple 
    ResClass = namedtuple('Res', 'count average') 
    # 子生成器 
    def averager(): 
        total = 0.0 
        count = 0 
        average = None 
        while True: 
            term = yield 
            if term is None: 
                break 
            total += term 
            count += 1 
            average = total / count 
        return ResClass(count, average) 
    # 委派生成器 
    def grouper(storages, key): 
        while True: 
            # 获取averager()返回的值 
            storages[key] = yield from averager() 
            
    # 客户端代码 
    def client(): 
        process_data = { 
            'boys_2': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], 
            'boys_1': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46] } 
        storages = {} 
        for k, v in process_data.items(): 
            # 获得协程 
            coroutine = grouper(storages, k) 
            # 预激协程 
            next(coroutine) 
            # 发送数据到协程 
            for dt in v: 
                coroutine.send(dt) 
                # 终止协程 
                coroutine.send(None) 
            print(storages)
    # run 
    client()
    
    
    {'boys_2': Res(count=1, average=36.3)}
    {'boys_2': Res(count=1, average=36.3), 'boys_1': Res(count=1, average=1.46)}
    

    相关文章

      网友评论

          本文标题:Python-学习之路-16 协程

          本文链接:https://www.haomeiwen.com/subject/uyfwpqtx.html