每当我不能百分百确定一个函数接受什么类型时,我都喜欢咨询typeshed,它是Python类型提示的规范存储库。例如,Mypy直接捆绑并使用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.contextmanagerdef foo() -> Iterator[None]: yield
那么,为什么
Generator[None, None, None]如评论所建议的那样使用也能起作用?
这是因为
Generator是的子类型
Iterator-我们可以通过咨询typeshed再次自行检查这一点。因此,如果我们的函数返回一个生成器,它仍然与
contextmanager预期的兼容,因此mypy可以毫无问题地接受它。



