Python3.9下用Union声明类型时mypy报不兼容错误
解决Python3.9下Mypy枚举字典类型不兼容问题
原始代码
from enum import Enum from typing import Dict, Union, Type class MyEnum(Enum): FIRST = 1, 'first' SECOND = 2 , 'second' __myenum_int_ref: Dict[int, MyEnum] = {k.value[0]: k for k in MyEnum} __myenum_str_ref: Dict[str, MyEnum] = {k.value[1]: k for k in MyEnum} __myenum_str_ref.update({k.name: k for k in MyEnum}) __flags_ref: Dict[Union[Type[str], Type[int]], Dict[Union[str, int], MyEnum]] = { str: __myenum_str_ref, int: __myenum_int_ref, }
Mypy报错信息
function.py:14: error: Dict entry 0 has incompatible type "Type[str]": "Dict[str, MyEnum]"; expected "Union[Type[str], Type[int]]": "Dict[Union[str, int], MyEnum]" function.py:15: error: Dict entry 1 has incompatible type "Type[int]": "Dict[int, MyEnum]"; expected "Union[Type[str], Type[int]]": "Dict[Union[str, int], MyEnum]"
问题原因
Mypy对字典类型的协变检查严格:Dict[str, MyEnum]并非Dict[Union[str, int], MyEnum]的子类型——前者键只能是str,后者允许str或int,类型约束不匹配。即便你明确知道str键对应的值只会接收str键的字典,Mypy默认也不会做上下文推断。
解决方案
方案1:用TypedDict精确映射结构(最优解)
通过TypedDict明确指定每个键对应的字典类型,类型最严谨:
from enum import Enum from typing import Dict, Type, TypedDict class MyEnum(Enum): FIRST = 1, 'first' SECOND = 2 , 'second' __myenum_int_ref: Dict[int, MyEnum] = {k.value[0]: k for k in MyEnum} __myenum_str_ref: Dict[str, MyEnum] = {k.value[1]: k for k in MyEnum} __myenum_str_ref.update({k.name: k for k in MyEnum}) class FlagsRef(TypedDict): str: Dict[str, MyEnum] int: Dict[int, MyEnum] __flags_ref: FlagsRef = { str: __myenum_str_ref, int: __myenum_int_ref, }
方案2:用cast做类型断言
不修改原有结构,手动告诉Mypy类型匹配:
from enum import Enum from typing import Dict, Union, Type, cast class MyEnum(Enum): FIRST = 1, 'first' SECOND = 2 , 'second' __myenum_int_ref: Dict[int, MyEnum] = {k.value[0]: k for k in MyEnum} __myenum_str_ref: Dict[str, MyEnum] = {k.value[1]: k for k in MyEnum} __myenum_str_ref.update({k.name: k for k in MyEnum}) __flags_ref: Dict[Union[Type[str], Type[int]], Dict[Union[str, int], MyEnum]] = { str: cast(Dict[Union[str, int], MyEnum], __myenum_str_ref), int: cast(Dict[Union[str, int], MyEnum], __myenum_int_ref), }
方案3:调整子字典类型注解
直接将子字典声明为Dict[Union[str, int], MyEnum],牺牲子字典的类型精确性换兼容性:
from enum import Enum from typing import Dict, Union, Type class MyEnum(Enum): FIRST = 1, 'first' SECOND = 2 , 'second' __myenum_int_ref: Dict[Union[str, int], MyEnum] = {k.value[0]: k for k in MyEnum} __myenum_str_ref: Dict[Union[str, int], MyEnum] = {k.value[1]: k for k in MyEnum} __myenum_str_ref.update({k.name: k for k in MyEnum}) __flags_ref: Dict[Union[Type[str], Type[int]], Dict[Union[str, int], MyEnum]] = { str: __myenum_str_ref, int: __myenum_int_ref, }
内容的提问来源于stack exchange,提问作者Lucas Coppio
相关产品推荐
相关产品推荐

