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 

posted @ 2019-08-22 17:21  colin220  阅读(132)  评论(0编辑  收藏  举报