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
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表达式用于获取一个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 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语句中指定多个上下文管理器。
使用异步上下文管理器能够很容易的实现用于数据库事务管理器的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): ...
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子句。