Python 类型提示和上下文管理器

新手上路,请多包涵

上下文管理器应该如何使用 Python 类型提示进行注释?

 import typing

@contextlib.contextmanager
def foo() -> ???:
    yield

contextlib 上的文档 没有过多提及类型。

关于 typing.ContextManager 的文档也不 是很有帮助。

还有 typing.Generator ,它至少有一个例子。这是否意味着我应该使用 typing.Generator[None, None, None] 而不是 typing.ContextManager

 import typing

@contextlib.contextmanager
def foo() -> typing.Generator[None, None, None]:
    yield

原文由 Peter 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 643
2 个回答

每当我不能 100% 确定函数接受什么类型时,我喜欢查阅 typeshed ,它是 Python 类型提示的规范存储库。例如,Mypy 直接捆绑并使用 typeshed 来帮助它执行类型检查。

我们可以在这里找到 contextlib 的存根: https ://github.com/python/typeshed/blob/master/stdlib/contextlib.pyi

 if sys.version_info >= (3, 2):
    class GeneratorContextManager(ContextManager[_T], Generic[_T]):
        def __call__(self, func: Callable[..., _T]) -> Callable[..., _T]: ...
    def contextmanager(func: Callable[..., Iterator[_T]]) -> Callable[..., GeneratorContextManager[_T]]: ...
else:
    def contextmanager(func: Callable[..., Iterator[_T]]) -> Callable[..., ContextManager[_T]]: ...

这有点让人不知所措,但我们关心的是这一行:

 def contextmanager(func: Callable[..., Iterator[_T]]) -> Callable[..., ContextManager[_T]]: ...

它声明装饰器接受一个 Callable[..., Iterator[_T]] 一个带有任意参数并返回一些迭代器的函数。所以总而言之,这样做会很好:

 @contextlib.contextmanager
def foo() -> Iterator[None]:
    yield

那么,为什么使用 Generator[None, None, None] 也像评论所建议的那样有效?

这是因为 GeneratorIterator 的子类型——我们可以 通过咨询 typeshed 再次检查自己。因此,如果我们的函数返回一个生成器,它仍然与 contextmanager 期望的兼容,所以 mypy 可以毫无问题地接受它。

原文由 Michael0x2a 发布,翻译遵循 CC BY-SA 4.0 许可协议

使用我的 PyCharm,我执行以下操作以使其类型提示起作用:

 from contextlib import contextmanager
from typing import ContextManager

@contextmanager
def session() -> ContextManager[Session]:
    yield Session(...)

UPD:请参阅下面的评论。看起来这个东西让 PyCharm 开心,但不是 mypy

原文由 kolypto 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题