You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何mypy提示子类属性类型不兼容?子类类型属父类联合子集仍报错

报错原因与修复方案

报错原因

  1. 属性类型的逆变要求:mypy对可赋值属性的类型检查遵循里氏替换原则(LSP),要求子类属性类型必须是父类类型的超类型(更宽泛),而非子类型(更严格)。父类ApiResponse的body类型是Union[OUTPUT_BASE_TYPES, Unset],可以接受OUTPUT_BASE_TYPES的任意实例或Unset;而子类CustomApiResponse将body重定义为MyDict,仅能接受特定的不可变字典类型,比父类类型更严格,违反了逆变要求,因此mypy报错。

  2. 递归类型的识别限制:你的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 12:15:58