Bai*_*ker 5 python contextmanager python-3.x python-3.5 mypy
什么是正确的类型签名__exit__?我有以下几点:
from types import TracebackType
from typing import Optional, Type
class Foo:
def __enter__(self) -> 'Foo':
return self
def __exit__(self, exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType]) -> bool:
return False
Run Code Online (Sandbox Code Playgroud)
在最近的mypy(0.560)上,此类型检查是否正确(--strict我对此签名有一定的信心,因为我从排版的内部偷了它)。
当此脚本与python 3.6一起运行时,按预期没有任何反应。但是当使用3.5.2运行时,我们会得到一个例外:
Traceback (most recent call last):
File "/home/student/mypy_test/test.py", line 4, in <module>
class Foo: #(ContextManager['Foo']):
File "/home/student/mypy_test/test.py", line 8, in Foo
def __exit__(self, exc_type: Optional[Type[BaseException]],
File "/usr/lib/python3.5/typing.py", line 649, in __getitem__
return Union[arg, type(None)]
File "/usr/lib/python3.5/typing.py", line 552, in __getitem__
dict(self.__dict__), parameters, _root=True)
File "/usr/lib/python3.5/typing.py", line 512, in __new__
for t2 in all_params - {t1} if not isinstance(t2, TypeVar)):
File "/usr/lib/python3.5/typing.py", line 512, in <genexpr>
for t2 in all_params - {t1} if not isinstance(t2, TypeVar)):
File "/usr/lib/python3.5/typing.py", line 1077, in __subclasscheck__
if super().__subclasscheck__(cls):
File "/home/student/.local/share/virtualenvs/sf_cs328-crowdsourced-QAuuIxFA/lib/python3.5/abc.py", line 225, in __subclasscheck__
for scls in cls.__subclasses__():
TypeError: descriptor '__subclasses__' of 'type' object needs an argument
Run Code Online (Sandbox Code Playgroud)
如果删除参数直到异常消失,我们会发现问题的类型是第一个:exc_type: Optional[Type[BaseException]]。
注意:如果类型签名不匹配(使用mypy运行时),要使其抱怨,您需要更改class Foo:为class Foo(ContextManager['Foo'])。我没有在片段做到这一点,因为typing在Python 3.5.2缺失Coroutine,Awaitable,ContextManager,等(这是老十岁上下的发行版的LTS版本的版本)。我在这里写了一个解决方法:https : //stackoverflow.com/a/49952293/568785。所以我想,完整的可复制示例是:
# Workaround for ContextManager missing in 3.5.2 typing
from typing import Any, TypeVar, TYPE_CHECKING
try:
from typing import ContextManager
except ImportError:
class _ContextManager:
def __getitem__(self, index: Any) -> None:
return type(object())
if not TYPE_CHECKING:
ContextManager = _ContextManager()
# The actual issue:
from types import TracebackType
from typing import Optional, Type
class Foo:
def __enter__(self) -> 'Foo':
return self
def __exit__(self, exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType]) -> bool:
return False
Run Code Online (Sandbox Code Playgroud)
我已经验证了ContextManager在Python 3.5.2 中无法继承运行仍会产生错误(因此,此异常不是此hack的产物,它是3.5.2运行时类型库的产物,不喜欢的签名__exit__)。
大概这是Python 3.5 typing库中的另一个错误。有解决这个问题的明智方法吗?
我现在使用的一个丑陋的TYPE_CHECKING解决方法是用来确定我是否应该伪造类型:
from typing import Type, TYPE_CHECKING
if TYPE_CHECKING:
BaseExceptionType = Type[BaseException]
else:
BaseExceptionType = bool # don't care, as long is it doesn't error
Run Code Online (Sandbox Code Playgroud)
然后你可以这样做:
def __exit__(self, exc_type: Optional[BaseExceptionType],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType]) -> bool:
return False
Run Code Online (Sandbox Code Playgroud)
已针对 Python 3.5.2 和 mypy 0.560 进行验证。
这当然会破坏任何 RTTI,但据我所知,RTTI 是 PEP 的一部分,直到 3.6 或 3.7 才(实验性地)落地。typing.get_type_hints()显然,这确实打破了。