关于Python异步上下文管理器方法__aenter__与__aexit__返回类型的定义疑问
__aenter__ and __aexit__ Great question—let's clear up the confusion around async context manager method return types, specifically for your MyService implementation.
First, let's address the core misunderstanding: the docs don't say __aenter__ and __aexit__ must return an awaitable directly. Instead, when implementing an async context manager, you have two valid options:
- Define the methods as
async def(which makes them return a coroutine, a built-in type of awaitable automatically) - Define them as regular
defmethods that explicitly return an awaitable object
Your current implementation uses the first (and more common) approach, which is perfectly valid. Now let's break down each method's return type in detail:
__aenter__ Return Type
For __aenter__, the goal is to return the object you want bound to the variable in async with MyService(...) as service:. In your case, that's the MyService instance itself.
Since you've defined __aenter__ as an async def method, Python automatically treats it as an awaitable. The return type annotation should reflect the value you get after awaiting the method—which is your MyService instance.
Your existing annotation -> "MyService" is correct. The string quote is needed because the MyService class isn't fully defined when the method is annotated (you can also use from __future__ import annotations in Python 3.7+ to avoid the quotes entirely).
__aexit__ Return Type
The __aexit__ method controls whether exceptions raised inside the async with block are suppressed or propagated:
- If it returns a truthy value (like
True), the exception is suppressed. - If it returns a falsy value (like
NoneorFalse), the exception propagates normally.
For async __aexit__ methods:
- The method itself is an awaitable, so Python will await it automatically.
- The return type annotation should reflect the value you get after awaiting—either
None(if you don't intend to suppress exceptions) orbool(if you might returnTrueto suppress exceptions in specific cases).
In your current code, you're returning None implicitly (since there's no explicit return statement), which is perfectly fine. Annotating it as -> None is correct. If you ever need to suppress exceptions (e.g., handle a specific error and return True), you can update the annotation to -> bool.
Updated Code with Correct Annotations
Here's your code with proper type hints (plus a stub MyAPI for completeness):
from typing import Any from __future__ import annotations # Optional, allows using MyService instead of "MyService" class MyAPI: async def init(self) -> None: pass async def close(self) -> None: pass class MyService: def __init__(self, my_api: MyAPI) -> None: self._my_api = my_api self._session_usage_count = 0 async def __aenter__(self) -> MyService: if not self._session_usage_count: await self._my_api.init() self._session_usage_count += 1 return self async def __aexit__(self, *exc: Any) -> None: self._session_usage_count -= 1 if not self._session_usage_count: await self._my_api.close()
Key Takeaways
__aenter__(async method): Annotate with the type of object you want to expose to theasync withblock (usuallyself's type).__aexit__(async method): Annotate withNoneif you don't suppress exceptions, orboolif you might returnTrueto suppress them.
内容的提问来源于stack exchange,提问作者Ivan Petrushenko

