输入提示 @asynccontextmanager 的返回值的正确方法是什么?

Eri*_*zke 12 python type-hinting contextmanager python-asyncio visual-studio-code

使用装饰器为函数的返回添加类型提示的正确方法是什么@asynccontextmanager?这是我所做的两次尝试,但都失败了。

from contextlib import asynccontextmanager
from typing import AsyncContextManager


async def caller():
    async with return_str() as i:
        print(i)

    async with return_AsyncContextManager() as j:
        print(j)


@asynccontextmanager
async def return_str() -> str:
    yield "hello"


@asynccontextmanager
async def return_AsyncContextManager() -> AsyncContextManager[str]:
    yield "world"
Run Code Online (Sandbox Code Playgroud)

对于 vscode 中的 Pylanceij Pylance显示类型Any。我考虑过的其他想法:

  • 我想也许我可以将类型信息传递给装饰器本身(例如@asynccontextmanager(cls=str),但我找不到任何示例,或者我可以传递的参数的任何描述。
  • async with return_str() as i: # type: str也不行。即使确实如此,我也宁愿暗示函数定义,而不是在每次调用时都暗示。类型注释不是很 DRY。
  • 我尝试创建一个具有和函数AsyncContextManager的类,但没有成功。如果它有效的话我会回到那个,但我更喜欢让装饰器工作,因为它更简洁。__aenter()____aexit()__

这是我将鼠标悬停在该return_AsyncContextManager()函数上的屏幕截图,并显示 Pylance 弹出窗口显示它返回AsyncContextManager[_T] 在此输入图像描述

Eri*_*zke 15

您必须提示AsyncIterator作为返回类型,如下所示:

@asynccontextmanager
async def my_acm() -> AsyncIterator[str]:
    yield "this works"


async def caller():
    async with my_acm() as val:
        print(val)
Run Code Online (Sandbox Code Playgroud)

这是因为该yield关键字用于创建生成器。考虑:

def a_number_generator():
    for x in range(10):  # type: int
        yield x

g = a_number_generator() # g is type Generator[int]
Run Code Online (Sandbox Code Playgroud)

考虑到@asynccontextgenerator 的类型提示,这是有意义的:
asynccontextmanager(func: Callable[..., AsyncIterator[_T]]) -> Callable[..., AsyncContextManager[_T]]

需要解析的内容有很多,但它表示asynccontextgenerator需要一个返回的函数AsyncIterator并将其转换为一个返回的新函数,并且还保留了AsyncContextManager 泛型类型。_T

这是一个屏幕截图,显示类型提示转移到调用者函数中。

在此输入图像描述