深刻Asyncio(四)Coroutines

Coroutines

asyncio在3.4版本添加到Python中,但经过async defawait关键字建立coroutines的语法是3.5才加入的,在这以前,人们把generators看成coroutines来使用,在一些老的语法中,你能够看到用@asyncio.coroutine之类的装饰器加yield的句式,在本文中,请摒弃过去的写法。bash

async def关键字

>>> async def f():    # 1
...     return 123
>>> type(f)    # 2
<class 'function'>
>>> import inspect    # 3
>>> inspect.iscoroutinefunction(f)
True
  1. 这是声明coroutine的最简单方式;
  2. 与生成器函数的断定同样,coroutine也会被断定为协程函数,只有在调用这个函数时才能得到协程,就如生成器同样;
  3. 标准库中的检查模块能够提供比type方法更好的内省能力。

正是因为在3.4版本用生成器看成协程来使用,因此在3.5版本中,async def的用法以及效果与生成器几乎相同。咱们经过代码来观察Python如何在不一样协程中切换,首先先看下如何得到return的值。async

当coroutine return的时候,将会抛出一个StopIteration异常。函数

>>> async def f():
...     return 123
>>> coro = f()
>>> try:
...     coro.send(None)    # 1
... except StopIteration as e:
...     print('The answer was: ', e.value)    # 2
The answer was:  123
  1. coroutine经过发送None来启动,这就是loop在背后作的事;
  2. 协程返回时抛出StopIteration异常,能够经过异常的value属性访问返回值。

协程的开始和结束是经过send()和StopIteration来定义的,loop负责在背后作这些事,开发者只须要为loop安排task便可。oop

await关键字

await关键字老是接收一个参数,其类型必须是awaitable的,必须是以下两种之一:
1. 一个coroutine;
2. 一个实现了__await__()方法的对象,这个方法必须返回一个迭代器。学习

async def f():
    await asyncio.sleep(1.0)
    return 123
async def main():
    result = await f()  # 1
    return result
  1. 这里调用了f函数返回了一个coroutine。

在开始学习loop以前,了解一下如何向协程提供异常颇有用,异常一般用于取消协程,在task.cancel()时,loop在内部使用coro.throw()来抛出一个asyncio.CancelledErrorcode

>>> coro = f()  # 使用上面的协程
>>> coro.send(None)
<Future pending>
>>> coro.throw(Exception, 'hello')  # 经过throw方法提供一个异常和值,该异常将在await处产生
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
Exception: hello
>>> async def f():
...     try:
...             while True: await asyncio.sleep(0)
...     except asyncio.CancelledError:
...             print('I was cancelled!')
...     else:
...             return 111
>>> coro = f()
>>> coro.send(None)
>>> coro.send(None)
>>> coro.throw(asyncio.CancelledError)
I was cancelled!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration   # 正是因为内部捕捉了CancelledError,这个协程得以正常退出
>>> import asyncio
>>> async def f():
...     try:
...             while True: await asyncio.sleep(0)
...     except asyncio.CancelledError:
...             print('Cancelled')
...             while True: await asyncio.sleep(0)  # 跳转到另外一个协程上去了
...     else: return 1
>>> coro = f()
>>> coro.send(None)
>>> coro.throw(asyncio.CancelledError)
Cancelled
>>> coro.send(None)

目前为止的代码中,都是经过手动调用send(None)throw(asyncio.CancelledError)来模拟loop的,下一章开始学习用loop来自动处理。协程

>>> async def f():
...     await asyncio.sleep(0)
...     return 123
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(f())
123
相关文章
相关标签/搜索