__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>
它没有返回None
!我们得到了一个协程对象,这是可等待的。
这些方法是async
函数,自动返回(可等待)的异步协程。在async
函数体中的return
语句决定了当您await
协程时返回什么,而不是调用函数时返回什么。
这类似于生成器函数返回生成器迭代器,即使它们通常没有return
语句,以及如果您将__iter__
编写为生成器函数,则不应尝试在生成器函数内部return
迭代器。
如果您在定义为
async
函数的
__aenter__
或
__aexit__
中放置
return
语句会发生什么?您可以这样做,如果这样做,
return
语句不必返回可等待对象。以下是Python的处理方式。
如果您从定义为
async
函数的
__aenter__
中
return
了某些内容,则决定将其绑定到
as
目标的内容(如果
async with
使用
as
)。
如果您从定义为
async
函数的
__aexit__
中
return
了某些内容,则确定是否抑制在块内引发的异常。"真值"表示告诉
async with
抑制异常,而"假值"表示告诉
async with
让异常传播。默认值
None
为假值,因此默认情况下不会抑制异常。
以下是一个示例:
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())
输出:
True
Exception got suppressed
async
函数返回本地协程对象。但是,考虑到async def f(): return 123
,几乎每个人都会说 "f
返回 123",而考虑到async def f(): return None
,几乎每个人都会说 "f
返回 None" - 因为这通常是“返回”的最有用的含义和抽象级别。在 Python 中,没有return
和return None
是相同的。 - mtraceur__aenter__
和__aexit__
必须返回可等待对象,而解决问题的关键是认识到这些__aenter__
和__aexit__
方法确实返回可等待对象,尽管缺少return
语句。话虽如此,我会扩展答案以澄清。 - user2357112