Python中的async with用法

原文出处:《async with和async for》

网上async with和async for的中文资料比较少,我把PEP 492中的官方陈述翻译一下。

异步上下文管理器”async with”
异步上下文管理器指的是在enter和exit方法处能够暂停执行的上下文管理器。

为了实现这样的功能,需要加入两个新的方法:aenter 和__aexit__。这两个方法都要返回一个 awaitable类型的值。

异步上下文管理器的一种使用方法是:

1
2
3
4
5
6
class AsyncContextManager:
    async def __aenter__(self):
        await log('entering context')

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

新语法
异步上下文管理器使用一种新的语法:

1
2
async with EXPR as VAR:
    BLOCK

这段代码在语义上等同于:

1
2
3
4
5
6
7
8
9
10
11
12
13
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表达式一样,可以在一个async with表达式中指定多个上下文管理器。

如果向async with表达式传入的上下文管理器中没有__aenter__ 和__aexit__方法,这将引起一个错误 。如果在async def函数外面使用async with,将引起一个SyntaxError(语法错误)。

例子
使用async with能够很容易地实现一个数据库事务管理器。

1
2
3
4
5
6
7
async def commit(session, data):
    ...

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

需要使用锁的代码也很简单:

1
2
async with lock:
    ...

而不是:

1
2
with (yield from lock):
    ...