深刻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
相關文章
相關標籤/搜索