如何使用Python类型提示注释上下文管理器?
typing.ContextManager
documentation on contextlib并未提及类型。
documentation on typing.ContextManager也不是那么有用。
还有typing.Generator,至少有一个例子。这是否意味着我应该使用import typing
@contextlib.contextmanager
def foo() -> typing.Generator[None, None, None]:
yield
而不是<?php
$title = 'Title here';
$short_url = 'http://shorturl.co';
$url = 'http://fullurl.com';
$twitter_params =
'?text=' . urlencode($title) . '+-' .
'&url=' . urlencode($short_url) .
'&counturl=' . urlencode($url) .
'';
$link = "http://twitter.com/share" . $twitter_params . "";
?>
<a href="<?php echo $link; ?>">Share on Twitter</a>
<?php
// rest of your code
?>
?
arr.push(val.players)
答案 0 :(得分:7)
每当我不能100%确定函数接受什么类型时,我想咨询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.contextmanager
def foo() -> Iterator[None]:
yield
那么,为什么使用Generator[None, None, None]
也会起作用,如评论所示?
这是因为Generator
是Iterator
的子类型 - 我们可以再次为自己检查by consulting typeshed。因此,如果我们的函数返回一个生成器,它仍然与contextmanager
期望的内容兼容,因此mypy会毫无问题地接受它。
答案 1 :(得分:3)
使用我的PyCharm,我可以执行以下操作来使其类型提示起作用:
Published
答案 2 :(得分:1)
要返回contextmanager的引用时,Iterator[]
版本不起作用。例如,以下代码:
from typing import Iterator
def assert_faster_than(seconds: float) -> Iterator[None]:
return assert_timing(high=seconds)
@contextmanager
def assert_timing(low: float = 0, high: float = None) -> Iterator[None]:
...
在return assert_timing(high=seconds)
行上将产生错误:
Incompatible return value type (got "_GeneratorContextManager[None]", expected "Iterator[None]")
该函数的任何合法用法:
with assert_faster_than(1):
be_quick()
将导致如下所示:
"Iterator[None]" has no attribute "__enter__"; maybe "__iter__"?
"Iterator[None]" has no attribute "__exit__"; maybe "__next__"?
"Iterator[None]" has no attribute "__enter__"; maybe "__iter__"?
"Iterator[None]" has no attribute "__exit__"; maybe "__next__"?
您可以像这样解决它...
def assert_faster_than(...) -> Iterator[None]:
with assert_timing(...):
yield
但是我将改用新的ContextManager[]
对象,并让装饰器的mypy静音:
from typing import ContextManager
def assert_faster_than(seconds: float) -> ContextManager[None]:
return assert_timing(high=seconds)
@contextmanager # type: ignore
def assert_timing(low: float = 0, high: float = None) -> ContextManager[None]:
...
答案 3 :(得分:1)
由上下文管理器包装的函数的返回类型为Iterator[None]
。
from contextlib import contextmanager
from typing import Iterator
@contextmanager
def foo() -> Iterator[None]:
yield
答案 4 :(得分:1)
基于 PEP-585,正确的注释类型似乎是 AbstractContextManager
(参见 https://www.python.org/dev/peps/pep-0585/#implementation)。比你可以使用以下代码:
import contextlib
@contextlib.contextmanager
def foo() -> contextlib.AbstractContextManager[None]:
yield
这是与 PyCharm 一起正确工作的唯一解决方案(与 typing.ContextManager
一起使用,但该解决方案应从 Python 3.9 中弃用)。当您在非常有用的 with
语句(类型提示)中使用它时,它会正确地帮助您。
但是当我回到最初的问题(“应该如何用 Python 类型提示注释上下文管理器?”)时,这取决于。从我的角度来看,正确的应该是我提到的那个。但这似乎不适用于 mypy(还)。有一些关于此 PEP 的更新(请参阅 https://github.com/python/mypy/issues/7907),但由于我对 mypy 的经验不多,我可能在这里遗漏了一些东西。