Python 3.5中__exit__类型注解运行报错但类型检查通过,求正确签名
__exit__类型注解运行报错的问题 首先,你遇到的问题是Python 3.5对特殊方法的类型注解支持不完善导致的——虽然你的签名完全符合静态类型规范(和typeshed里的标准实现一致),但Python 3.5的解释器在运行时无法正确处理Type[BaseException]这类泛型类型注解,而mypy作为静态检查器,只做静态分析不执行代码,所以能顺利通过检查。
为什么运行时报错?
Python 3.5是第一个支持PEP 484类型注解的版本,但对泛型类型(比如Type[T])的运行时处理还存在局限。当解释器加载带有Optional[Type[BaseException]]注解的__exit__方法时,会尝试解析这个类型构造,而3.5的运行时环境还不支持这种泛型注解的直接实例化,因此抛出错误。
针对Python 3.5的解决方案
这里有两种可靠的方式来修复运行时错误,同时保留静态类型检查的有效性:
1. 使用字符串形式的延迟求值注解
把类型注解用字符串包裹,这样Python运行时不会立即解析类型,只会把注解当作字符串存储,既避免了运行时错误,也不影响mypy的静态检查:
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
2. 用type替代Type[BaseException](宽松但兼容)
如果不想用字符串注解,可以把exc_type的类型换成Optional[type]——虽然静态检查时会稍微宽松一点(允许任何异常类型的元类),但完全兼容Python 3.5的运行时:
from types import TracebackType from typing import Optional class Foo: def __enter__(self) -> 'Foo': return self def __exit__(self, exc_type: Optional[type], exc_value: Optional[BaseException], traceback: Optional[TracebackType]) -> bool: return False
标准的__exit__类型签名
你参考typeshed实现的签名其实就是PEP 484规定的标准签名,适用于Python 3.6及以上版本(这些版本对泛型注解的运行时支持更完善):
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
在Python 3.6+中,你还可以通过from __future__ import annotations让所有注解自动延迟求值,无需手动写字符串注解。
内容的提问来源于stack exchange,提问作者Bailey Parker

