原文出处:《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): ... |