为何mypy提示子类属性类型不兼容?子类类型属父类联合子集仍报错
报错原因与修复方案
报错原因
属性类型的逆变要求:mypy对可赋值属性的类型检查遵循里氏替换原则(LSP),要求子类属性类型必须是父类类型的超类型(更宽泛),而非子类型(更严格)。父类
ApiResponse的body类型是Union[OUTPUT_BASE_TYPES, Unset],可以接受OUTPUT_BASE_TYPES的任意实例或Unset;而子类CustomApiResponse将body重定义为MyDict,仅能接受特定的不可变字典类型,比父类类型更严格,违反了逆变要求,因此mypy报错。递归类型的识别限制:你的
OUTPUT_BASE_TYPES是递归定义的(包含immutabledict[str, 'OUTPUT_BASE_TYPES']),mypy 1.4.1版本对这类递归类型的子类型推导支持有限,无法自动识别MyDict(继承自immutabledict[str, Union[None, str]])属于OUTPUT_BASE_TYPES的子类型,进一步加剧了类型不兼容的判定。
修复方法
方法1:将body设为只读属性
如果body属性不需要被重新赋值,可以用@property装饰器将其定义为只读属性。mypy对只读属性允许协变类型(子类属性类型可以是父类的子类型),符合LSP的只读场景要求:
from typing import Union, Unset import immutabledict OUTPUT_BASE_TYPES = Union[str, None, immutabledict.immutabledict[str, 'OUTPUT_BASE_TYPES']] class ApiResponse: @property def body(self) -> Union[OUTPUT_BASE_TYPES, Unset]: ... class MyDict(immutabledict.immutabledict[str, Union[None, str]]): ... class CustomApiResponse(ApiResponse): @property def body(self) -> MyDict: ...
方法2:用泛型重构父类
通过泛型让父类支持自定义body类型,既满足子类的类型约束,又符合类型兼容性要求:
from typing import Union, Unset, TypeVar, Generic import immutabledict OUTPUT_BASE_TYPES = Union[str, None, immutabledict.immutabledict[str, 'OUTPUT_BASE_TYPES']] T = TypeVar('T', bound=OUTPUT_BASE_TYPES) class ApiResponse(Generic[T]): def __init__(self, body: Union[T, Unset]): self.body = body class MyDict(immutabledict.immutabledict[str, Union[None, str]]): ... class CustomApiResponse(ApiResponse[MyDict]): def __init__(self, body: MyDict): super().__init__(body)
方法3:显式声明类型兼容(不推荐)
如果确定代码逻辑安全,可以用typing.cast绕过mypy的检查,但这会失去静态类型检查的保障:
from typing import cast class CustomApiResponse(ApiResponse): def __init__(self, body: MyDict): self.body = cast(Union[OUTPUT_BASE_TYPES, Unset], body)
内容的提问来源于stack exchange,提问作者spacether
相关产品推荐
相关产品推荐

