zoukankan      html  css  js  c++  java
  • Python:协程

    协程

    可迭代、迭代器、生成器三者区别:

    • 可迭代对象可以使用for循环。其内部实现了 __iter__ 这个魔术方法。例如:字符串、list、tuple、list等。
    • 迭代器对比可以迭代对象,多了一个函数 __next__ 这样我们既可以使用for循环来间断获取元素值,也可以直接使用next()方法来实现。
    • 生成器则是在迭代器的基础上(可以用for循环,可以用next()),在实现了yield

    yield可以理解为函数中的return。在每次next(),或者for遍历的时候,都会yield这里将新的值返回回去,并在这里阻塞,等待下一次的调用。

    创建一个生成器有如下两种方法:

    • 使用列表生成式
    • 实现yield的函数

    可迭代对象和迭代器,是将所有的值都生成存放在内存中;而生成器则是需要元素才临时生成,节省时间,节省空间。

    from collections.abc import Iterable, Iterator, Generator
    
    # 列表生成器
    L = (x * x for x in range(10))
    print(L)
    print(isinstance(L, Iterable))
    print(isinstance(L, Iterator))
    print(isinstance(L, Generator))
    
    
    # 实现yield的函数
    def mygen(n):
        now = 0
        while now < n:
            yield now
            now += 1
    gen = mygen(4)
    print(next(gen))
    print(gen.send(None))
    print(next(gen))
    print(gen.send(None)) 

    由于生成器并不是一次生成所有元素,而是一次一次的执行返回,那么如何刺激生成器执行(或者说激活)呢?激活主要有两个方法:

    • 使用 next(generator)
    • 使用 generator.send(None)

    生成器在其生命周期中,会有如下四个状态:

    • GEN_CREATED # 等待开始执行
    • GEN_RUNNING # 解释器正在执行(只有在多线程应用中才能看到这个状态)
    • GEN_SUSPENDED # 在yield表达式处暂停
    • GEN_CLOSED # 执行结束

    生成器在工作过程中,若生成器不满足生成元素的条件,就会应该抛出异常(StopIteration),我们在自定义生成器的时候,在不满足生成元素时,抛出异常。

    from inspect import getgeneratorstate
    
    
    def mygen(n):
        now = 0
        while now < n:
            yield now
            now += 1
        raise StopIteration
    
    
    if __name__ == '__main__':
        gen = mygen(2)
        print(getgeneratorstate(gen))
    
        print(next(gen))
        print(getgeneratorstate(gen))
    
        print(next(gen))
        gen.close()  # 手动关闭/结束生成器
        print(getgeneratorstate(gen))

    如何向生成器发送消息,就是用send(100)方法:

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

    上面的例子中,通过yield将函数分成各部分:

    • yield index 是将index return给外部调用程序
    • jump = yield 可以接收外部程序通过send()发送的信息,并赋值给jump

    协程:为非抢占式多任务产生子程序的计算机程序组件,协程允许不同入口点在不同位置暂停或开始执行程序。

    子程序,或者称为函数,在所有语言中都是层级调用,例如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。子程序调用是通过栈实现的,一个线程就是执行一个子程序。子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。

    yield from 后面需要加的是可迭代对象,它可以是普通的可迭代对象,迭代器,或者生成器。

    asyncio 是python3.4版本引入的标准库,直接内置了对异步IO的支持。

    和其相关的概念:

    • event_loop 事件循环:程序开启一个无限的循环,程序员会把一些函数(协程)注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。
    • coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。
    • future 对象: 代表将来执行或没有执行的任务的结果。它和task上没有本质的区别
    • task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。Task 对象是 Future 的子类,它将 coroutine 和 Future 联系在一起,将 coroutine 封装成一个 Future 对象。
    • async/await 关键字:python3.5 用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。其作用在一定程度上类似于yield。

    参考文档:

    https://www.cnblogs.com/wongbingming/p/9085268.html 

  • 相关阅读:
    Codeforces Round #605 (Div. 3)E
    Codeforces Round #628 (Div. 2)
    Codeforces Round #627 (Div. 3)
    AC自动机,知识点+hdu模板题
    Tire树,hdu2846,hdu4825
    Educational Codeforces Round 83 (Rated for Div. 2)
    分层最短路
    初入mysql的学习
    赛后总结
    Codeforces Round #625 (Div. 2, based on Technocup 2020 Final Round) D
  • 原文地址:https://www.cnblogs.com/colin220/p/11395567.html
Copyright © 2011-2022 走看看