mypy处理Union类型参数转换时未识别类型判断报类型错误
核心原因
mypy 触发类型收窄(也就是根据条件判断自动缩小变量的类型范围,让后续代码的类型匹配检查更精准)的逻辑,默认仅可靠识别基于isinstance()的类型判断分支,不支持你代码里写的type(hook) is not list这类直接对比原生类型的判断。
你当前启用的check_untyped_defs、disallow_incomplete_defs配置只是强化了类型检查的严格程度,并不是触发这个报错的核心原因——哪怕在最宽松的检查模式下,mypy也不会对type(x) is xxx的判断做自动类型收窄。
这就导致静态检查的时候,mypy始终认为hook变量的类型是完整的HookType联合类型:
- 你在判断分支里写
hook = [hook]时,mypy不会认为此时的hook已经是单个可调用对象,反而觉得列表里放的是「可调用对象/可调用列表」的联合类型,和预期的列表元素类型Callable[[str], str]不匹配,报第一个错误 - 跳过判断分支时,mypy也不会认为此时的
hook已经是列表类型,依然把它的类型记为完整的联合类型,后续给self.hook赋值时,就会出现联合类型和目标列表类型不兼容的第二个错误
另外补充一个设计层面的原因:type(x) is list的判断本身也不符合Python的通用类型判断规范——如果传入的是list的子类实例,type(x) is list会返回False,但isinstance(x, list)会返回True,这种边界场景的不可靠性,也是mypy不对这类判断做自动类型收窄的原因。
修复方式
把类型判断逻辑改成基于
isinstance的写法,让mypy可以正常触发类型收窄:
原判断:if type(hook) is not list: hook = [hook]修改为:
if not isinstance(hook, list): hook = [hook]修改后mypy会自动完成类型推导:进入if分支时
hook类型被收窄为单个可调用对象,跳过分支时hook类型被收窄为可调用对象列表,完全匹配后续的赋值要求。额外修正一个代码里的常见坑:不要使用可变对象作为函数默认参数。你当前代码里写的
hook: HookType = []是Python经典的可变默认参数陷阱——所有不传hook参数的实例会共享同一个列表对象,很容易出现意料之外的状态污染。正确的写法是把默认值设为None,在函数内部初始化默认值:def __init__( ... hook: HookType | None = None, ): ... if hook is None: hook = [] if not isinstance(hook, list): hook = [hook] self.hook: List[Callable[[str], str]] = hook
内容的提问来源于stack exchange,提问作者cacharle

