Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

18 Jul 2020

Python的PEP492:添加Coroutine支持

PEP 492 – Coroutines with async and await syntax为Python 3.5添加了通用的Coroutine支持。Coroutine是为了让书写异步代码像书写同步代码那样自然。

PEP 492有一个假设:

This PEP assumes that the asynchronous tasks are scheduled and coordinated by an Event Loop similar to that of stdlib module asyncio.events.AbstractEventLoop.

但是其实Python之前是已经具有Coroutine功能,可以使用yield或者yield from关键字从一个函数临时返回。但是采用yield的Coroutine一般是用来做Generator,也就是数据生成器。不是为了并发操作。而新引入的Coroutine是基于之前的Generator Coroutine实现的,但是主要是为了异步和并发。

在Python文档的3. Data model¶中,旧的,基于yield的coroutine被称为Generator functions,新的通用的Coroutine则称为Coroutine functions,然后基于新的Coroutine做成的generator叫做Asynchronous generator functions。

Generator functions和Coroutine functions类似,返回的iterator对象都具有以下方法,来与Coroutine的实例交互:

  • send()
  • throw()
  • close()

不同的是,Generator functions返回的对象具有__next__()方法,可以遍历;而Coroutine functions返回的对象具有__await__()方法,是awaitable的(只能await一次)。然后Coroutine functions定义的时候必须加上async:

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

在上面的read_data函数中可以使用await关键字,但是不能使用yield。如果Coroutine没有被await,否则垃圾回收的时候就会出现RuntimeWarning异常。

使用types.coroutine()可以把Generator functions修饰成Coroutine functions:

@types.coroutine
def process_data(db):
    data = yield from read_data(db)
    ...

Coroutine functions都是awaitable的,普通对象也可以通过__await__()来把自己变成awaitable。__await__()必须返回一个iterator,否则就会产生TypeError。

使用async with可以产生一个异步的context manager。async with的使用对象必须具有__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__

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

相应的,也有async for语句,用于含有__aiter____anext__方法的对象:

class AsyncIterable:
    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):
        ...

New Standard Library Functions描述了针对标准库的改动,主要包括对inspect模块的修改,以及增加了sys.set_coroutine_wrapper(wrapper) 和sys.get_coroutine_wrapper()等调试功能。

collections.abc.Awaitable,collections.abc.Coroutine,collections.abc.AsyncIterable,collections.abc.AsyncIterator 为新增的抽象基类。

Design Considerations章节有一些对设计上取舍的描述。

其他参考

Categories

Tags

comments powered by Disqus