抽象基类多派生实例生成映射触发mypy错误的解决咨询
解决mypy对抽象基类派生类容器的类型提示报错问题
最小可复现代码
from abc import ABC, abstractmethod from typing import Dict, Type class I(ABC): @abstractmethod def do(self) -> None: pass class A(I): def do(self) -> None: pass class B(I): def do(self) -> None: pass class C(I): def do(self) -> None: pass # 无报错 mapping_1: Dict[str, Type[I]] = {"a": A, "b": B} # 触发报错的映射 mapping_2 = {"a": A, "b": B, "c": C} mapping_3: Dict[str, Type[I]] = {"a": A, "b": B, "c": C} mapping_4: Dict[str, Type[A] | Type[B] | Type[C]] = {"a": A, "b": B, "c": C} mapping_5: Dict[str, Type[I]] = {"a": A, "b": B, "c": C}
mypy错误信息示例
error: Incompatible types in assignment (expression has type "Dict[str, Type[A] | Type[B] | Type[C]]", variable has type "Dict[str, Type[I]]") [assignment]
解决方案
1. 使用绑定抽象基类的协变TypeVar
这是最推荐的方案,能明确类型兼容关系,同时保持扩展性:
from abc import ABC, abstractmethod from typing import Dict, Type, TypeVar # 定义协变TypeVar,绑定到抽象基类I I_Type = TypeVar("I_Type", bound=I, covariant=True) class I(ABC): @abstractmethod def do(self) -> None: pass # 派生类定义不变 # 所有映射均可正常通过类型检查 mapping_2: Dict[str, Type[I_Type]] = {"a": A, "b": B, "c": C} mapping_3: Dict[str, Type[I_Type]] = {"a": A, "b": B, "c": C} mapping_5: Dict[str, Type[I_Type]] = {"a": A, "b": B, "c": C}
2. 显式声明联合类型(适合派生类固定的场景)
如果派生类数量固定且不会新增,可直接用Union列出所有子类的类型:
from typing import Union mapping_4: Dict[str, Union[Type[A], Type[B], Type[C]]] = {"a": A, "b": B, "c": C}
3. 升级mypy版本
部分旧版本mypy(如1.11.2)在处理多派生类的Type协变时存在兼容性问题,升级到最新稳定版(如1.13及以上)可能自动修复该报错。
4. 临时忽略报错(不推荐)
如果只是临时绕过检查,可添加type: ignore注释,但会失去类型校验能力:
mapping_3: Dict[str, Type[I]] = {"a": A, "b": B, "c": C} # type: ignore[assignment]
问题原因
mypy对Type[I]和Type[A] | Type[B] | Type[C]的协变关系推断存在版本差异:当派生类数量为2时,mypy会自动识别为兼容Type[I];但数量≥3时,旧版本无法正确推导这种兼容关系。使用绑定的TypeVar可以明确告知mypy,所有子类类型均属于I的子类型范畴,从而通过校验。
内容的提问来源于stack exchange,提问作者oliversm
相关产品推荐
相关产品推荐

