0x03 await和async与协程
协程与生成器
之前是用生成器来实现的协程的功能, 但其本质还是一个生成器. 这样容易与作为只作为生成器使用的情况, 容易出现混淆的情况. 为了避免这种情况, 需要在定义时与生成器加以区别.
有两种方法:
基于生成器的协程: 使用asyncio.coroutine或types.coroutine装饰器来标注协程
原生协程: 以async def定义的协程
基于生成器的协程定义如下:
输出为:
可以看出仍是生成器对象, 只是这种定义的方法让这种生成器区别于专门的生成器.
原生协程的定义如下:
分别创建下:
结果为:
可以看到两者的区别. 与基于生成器定义的协程在定义上的区别在于:
定义时使用async声明是原生协程
在定义体内不能使用yield, 而是使用await进行代替
使用方式仍是一样的, 如使用send
, throw
, close
等方法.
另外两者的区别还在于:
原生协程定义里不能用yield或yield from表达式
原生协程没有 iter 和 next 方法, 而是使用 await 方法.
基于生成器的协程中不能使用yield from原生协程, 原因在于上面一条
反过来, 原生协程可以使用await基于生成器的协程
原生协程的调用
原生协程的调用有两种方法:
await: 使用await表达式来调用协程, 用法和yield from类似, 因此只能再原生协程内部使用, 且只能接受awaitable对象. awaitable对象就是其 await 对象返回一个迭代器对象.
原生协程和基于生成器的协程都是awaitable对象
send方法: 和基于生成器的协程一样, 可以调用协程对象的send方法进行调用, 用在非协程函数的定义里.
因此整个调用代码的结构类似于:
原生协程的问题
原生协程不能真正的暂停执行并强制性返回给事件循环
假设事件循环在main
函数里, 原生协程是native_coroutine
, 怎么做才能让原生协程暂停, 并返回到main
函数里?
native_coroutine
里面不能使用yield
, 只能使用await
, 这只会进入到更深的协程里. 因此只能使用return
或raise
方法返回到main
, 但这种方法不是暂停, 而是退出, 因此再也回不到native_coroutine
协程里了.
因此, 对于异步编程, 最外层的事件循环(main
)如果需要调用协程send
方法, 则大部分的异步方法都可以用原生协程来实现, 但最底层的异步方法则需要用基于生成器的协程.
参考资料
最后更新于