协程
可迭代、迭代器、生成器三者区别:
- 可迭代对象可以使用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。
参考文档: