Python 3.5的async和await特性(PEP492翻譯)


緣由:

 1,coroutine容易與正常的generators弄混python

 2,一個function是否爲coroutine由函數體內是否有yield 或者yield from 決定,這不科學。數據庫

 3,若是在語法上容許yield的地方纔能進行異步調用,那諸如with和for語句中都不能執行異步了。session

咋解決呢,把coroutine當成一個native的Python語言特性,與generator徹底獨立。框架

Native coroutines及其新的語法使得在異步條件下定義context manager(上下文管理器)和iteration protocols(迭代器協議)成爲可能(也就是with和for了)。異步

經過 async with語句可使得Python程序在進入和退出runtime context(運行時上下文)時,執行異步調用;async

經過 async for 語句使得能夠在迭代器中執行異步調用。(老外真是的,總是 make it possible).函數

 

語法定義

假定你已經知道:fetch

 * Python中coroutines的實現。 implementation of coroutines in Python ( PEP 342 and PEP 380 ). this

 * 一些要被改變的語法來自asyncio框架和"Cofunctions"提議(已經悲劇了)。Motivation for the syntax changes proposed here comes from the asyncio framework ( PEP 3156 ) and the "Cofunctions" proposal ( PEP 3152 , now rejected in favor of this specification).spa

新定義的coroutine

async def read_data(db):
    pass

native couroutines的關鍵特性:

* 使用async def定義的函數老是native coroutine,不管其中是否有await表達式。

* async函數中不容許有yield和yield from,將拋出SyntaxErro異常。

* 在內部呢,引入了兩個新的code object flags. 

     -- CO_COROUTINE用於標記native corroutine(也就是經過async def 定義的)

     -- CO_ITERABLE_COROUTINE 用於的基於 生成器的coroutine與native coroutines兼容。

   全部的coroutine對象都有CO_GENERATOR標準。

* generator返回generator object, coroutines 返回 coroutine object

* 沒有被await on的coroutine在gc時會拋出RuntimeWarning 。

Await表達式

await表達式用於獲取一個coroutine的執行結果。

async def read_data(db):
    data = await db.fetch('SELECT ...')
    ...

await,與yield from相似(譯註;其實知道的真是很少),將阻塞read_data的執行,直到db.fetch這一awaitable的完成並返回數據。

awaitable(注:主要這個awaitable是名詞,不是形容詞)能夠是: 

1, 從一個native coroutine函數返回的native coroutine object.

2, 以types.coroutine 裝飾的(decorated) 生成器函數返回的generator-based coroutine object。

3,一個對象,該對象的__await__方法返回一個迭代器。

    若是__await__返回的不是iterator,則拋出TypeError。

4,CPython的C API定義 tp_as_async->am_await函數。

    若是await出如今async def函數之外,則拋出Syntax Error;

 將awaitable對象之外的任何東西傳遞給await表達式都會拋出TypeError。

。。。。。。。。。

...忽略嚴格的語法定義部分...

。。。。。。。。

await表達式的優先級高於**,低於切片[]、函數調用()和attribute reference(屬性引用,如x.attribute),


Asynchronous Context Managers and "async with"

asynchronous context manager(異步上下文管理器)是可以在enter和exit方法中阻塞(當前coroutine)執行的上下文管理器。又增長了兩個魔力函數:__aenter__ 和__aexit__ ,兩個函數都必須返回一個awaitable對象。

舉個例子:

class AsyncContextManager:
    async def __aenter__(self):
        await log('entering context')

    async def __aexit__(self, exc_type, exc, tb):
        await log('exiting context')

 新的語法:

提出針對異步上下文管理器的新語法定義:

async with EXPR as VAR:
    BLOCK

在語法上等價於:

mgr = (EXPR)
aexit = type(mgr).__aexit__
aenter = type(mgr).__aenter__(mgr)
exc = True

VAR = await aenter
try:
    BLOCK
except:
    if not await aexit(mgr, *sys.exc_info()):
        raise
else:
    await aexit(mgr, None, None, None)

和一般的with語句同樣,能夠在一個await with語句中指定多個上下文管理器。

Example

使用異步上下文管理器能夠很容易的實現用於數據庫事務管理器的coroutine. 

With asynchronous context managers it is easy to implement proper database transaction managers for coroutines:

async def commit(session, data):
    ...

    async with session.transaction():
        ...
        await session.update(data)
        ...

須要加鎖的代碼變得更加清晰:Code that needs locking also looks lighter:

async with lock:
    ...

instead of:

with (yield from lock):
    ...


異步迭代器和"async for"

asynchronous iterable可以在其iter實現中調用異步代碼,而且可以在其next方法中調用異步代碼。

* 必須實現__aiter__方法,該方法返回一個awaitable,而且該awaitable的結果必須 是一個asynchronous iterator object。An object must implement an __aiter__ method returning an awaitable resulting in an asynchronous iterator object .

* asynchronous iterator object必須實現 __anext__ 成員函數,該成員函數返回 awaitable對象 ;

 * 爲中止迭代,__anext__必須拋出StopAsyncIteration 異常。

舉個例子:

class AsyncIterable:
    async def __aiter__(self):
        return self

    async def __anext__(self):
        data = await self.fetch_data()
        if data:
            return data
        else:
            raise StopAsyncIteration

    async def fetch_data(self):
        ...

新語法

A new statement for iterating through asynchronous iterators is proposed:

async for TARGET in ITER:
    BLOCK
else:
    BLOCK2

等價於:

iter = (ITER)
iter = await type(iter).__aiter__(iter)
running = True
while running:
    try:
        TARGET = await type(iter).__anext__(iter)
    except StopAsyncIteration:
        running = False
    else:
        BLOCK
else:
    BLOCK2

若是用於async for的迭代器沒有__aiter__ 成員函數,將拋出TypeError; 

若是在async def函數之外使用async for將拋出SyntaxError錯誤。

如同一般的 for語句,async for也有可選的else子句。

相關文章
相關標籤/搜索