python 异步上下文管理器

Z. *_*Qui 4 python asynchronous contextmanager

在 Python Lan 参考文献中。3.4.4,据说__aenter__()并且__aexit__()必须返回awaitables。但是,在示例异步上下文管理器中,这两个方法返回 None:

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

    async def __aexit__(self, exc_type, exc, tb):
        await log('exiting context')
Run Code Online (Sandbox Code Playgroud)

这段代码正确吗?

use*_*ica 7

__aenter__并且__aexit__必须返回等待对象,但是看看当您调用示例中的等待对象时会发生什么:

>>> class AsyncContextManager:
...     async def __aenter__(self):
...         await log('entering context')
...     async def __aexit__(self, exc_type, exc, tb):
...         await log('exiting context')
... 
>>> AsyncContextManager().__aenter__()
<coroutine object AsyncContextManager.__aenter__ at 0x7f5b092d5ac0>
Run Code Online (Sandbox Code Playgroud)

它没有回来None!我们得到了一个协程对象,它是可等待的。

这些方法是async函数,它们自动返回(可等待的)异步协程。return函数体中的语句async决定了协程时返回的内容await,而不是调用函数时返回的内容。

这类似于生成器函数如何返回生成器迭代器,即使它们通常没有return语句,以及如果您编写__iter__为生成器函数,则不应尝试return在生成器函数内使用迭代器。


那么,如果您确实将 a 放入return__aenter__定义__aexit__async函数,会发生什么情况?好吧,您可以,如果您这样做,该return语句不必返回可等待的。这就是 Python 将要做的事情。

如果您将return某些内容__aenter__定义为async函数,那么它决定了绑定到as目标的内容(如果async with使用as.

如果您将return某些内容__aexit__定义为async函数,则确定是否抑制块内引发的异常。“真”值告诉async with抑制异常,而“假”值告诉async with让异常传播。默认None值为 false,因此默认情况下不会抑制异常。

这是一个例子:

import asyncio

class Example:
    async def __aenter__(self):
        return 3
    async def __aexit__(self, exc_type, exc, tb):
        return True

async def example():
    async with Example() as three:
        print(three == 3)
        raise Exception
    print("Exception got suppressed")

asyncio.run(example())
Run Code Online (Sandbox Code Playgroud)

输出:

True
Exception got suppressed
Run Code Online (Sandbox Code Playgroud)

  • @mtraceur:我不同意;低层实施细节对于解决误解至关重要。语言参考说“__aenter__”和“__aexit__”必须返回等待对象,解决问题中的误解的关键是认识到这些“__aenter__”和“__aexit__”方法实际上确实返回等待对象,尽管缺少“返回`语句。也就是说,我将扩展答案以进行澄清。 (2认同)