上下文管理器应如何使用Python类型提示进行注释?
import typing
@contextlib.contextmanager
def foo() -> ???:
yield
documentation on contextlib没有太多提及类型。
documentation on typing.ContextManager也不是那么有用。
还有typing.Generator,至少有一个例子。这是否意味着我应该使用
typing.Generator[None, None, None]
而不是typing.ContextManager
?import typing
@contextlib.contextmanager
def foo() -> typing.Generator[None, None, None]:
yield
最佳答案
每当我不确定100%是否接受函数的类型时,我都喜欢咨询typeshed,它是Python类型提示的规范存储库。例如,Mypy直接 bundle 并使用typeshed帮助其执行类型检查。
我们可以在此处找到contextlib的存根:https://github.com/python/typeshed/blob/master/stdlib/2and3/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]
也可以呢?这是因为
Generator
是Iterator
的子类型-我们可以再次为自己检查by consulting typeshed。因此,如果我们的函数返回一个生成器,它仍然与contextmanager
期望的兼容,因此mypy可以毫无问题地接受它。