使用字典推导与isinstance时mypy报返回值类型不兼容错误求助
MyPy类型错误修复:返回值类型不兼容问题
错误信息
Incompatible return value type (got "Dict[str, Pin]", expected "Union[Dict[str, Input], Dict[str, Output]]") [return-value]mypy
示例代码
from typing import Dict, List, Union, Type class Pin(): def __init__(self): pass class Input(Pin): def __init__(self): pass class Output(Pin): def __init__(self): pass INPUT_STRINGS: List[str] = ["in", "input", "i"] OUTPUT_STRINGS: List[str] = ["out", "output", "o"] def filter_pins(pins: Dict[str, Pin], which) -> Union[Dict[str, Input], Dict[str, Output]]: direction: Union[Type[Output], Type[Input]] if which.lower() in OUTPUT_STRINGS: direction = Output elif which.lower() in INPUT_STRINGS: direction = Input filtered = {name: pin for name, pin in pins.items() if isinstance(pin, direction)} return filtered
问题原因
MyPy无法在字典推导的上下文中,对direction这个Union类型变量做精确的类型收窄。尽管你已经通过条件分支给direction赋值了具体的子类,但MyPy在处理字典推导时,仍然把direction当成Union[Type[Input], Type[Output]]看待,因此无法推断出筛选后的pin必然是Input或Output实例,只能识别为父类Pin,导致返回的字典类型与声明的返回值类型不匹配。这属于MyPy对复杂上下文类型推断的限制,并非Bug。
修复方案
方案1:拆分逻辑到条件分支(推荐)
将字典推导放到对应的if/elif分支中,让MyPy能明确每个分支返回的具体字典类型:
from typing import Dict, List, Union, Type class Pin(): def __init__(self): pass class Input(Pin): def __init__(self): pass class Output(Pin): def __init__(self): pass INPUT_STRINGS: List[str] = ["in", "input", "i"] OUTPUT_STRINGS: List[str] = ["out", "output", "o"] def filter_pins(pins: Dict[str, Pin], which: str) -> Union[Dict[str, Input], Dict[str, Output]]: which_lower = which.lower() if which_lower in OUTPUT_STRINGS: return {name: pin for name, pin in pins.items() if isinstance(pin, Output)} elif which_lower in INPUT_STRINGS: return {name: pin for name, pin in pins.items() if isinstance(pin, Input)} # 处理非法输入的默认分支 raise ValueError(f"Invalid direction: {which}")
每个分支直接使用具体的子类进行筛选,MyPy能准确推断返回的字典类型,完全符合声明的返回值要求。
方案2:使用类型强制转换
如果不想拆分逻辑,可以用typing.cast手动向MyPy断言筛选后的字典类型:
from typing import Dict, List, Union, Type, cast # 类定义部分不变 def filter_pins(pins: Dict[str, Pin], which: str) -> Union[Dict[str, Input], Dict[str, Output]]: direction: Union[Type[Output], Type[Input]] which_lower = which.lower() if which_lower in OUTPUT_STRINGS: direction = Output filtered = cast(Dict[str, Output], {name: pin for name, pin in pins.items() if isinstance(pin, direction)}) elif which_lower in INPUT_STRINGS: direction = Input filtered = cast(Dict[str, Input], {name: pin for name, pin in pins.items() if isinstance(pin, direction)}) else: raise ValueError(f"Invalid direction: {which}") return filtered
注意:使用cast相当于手动跳过MyPy的类型检查,需确保业务逻辑绝对正确,避免引入类型安全隐患。
内容的提问来源于stack exchange,提问作者mtytgat
相关产品推荐
相关产品推荐

