为何mypy中同一类型_CT被识别为非自身?
问题分析与解决
问题描述
以下代码试图实现工具函数lt_through,其返回的函数可作为类方法,让类的<运算符先将实例转换为指定的conv类型后再比较。但在严格模式下运行mypy时出现错误:
from typing import Callable, TypeVar, Type, Protocol from typing_extensions import Self _CT = TypeVar("_CT") _T = TypeVar("_T") class LtConverter(Protocol): def __new__(cls: Type[_T], x: _CT) -> _T: ... def __lt__(self, other: Self) -> bool: ... def lt_through(conv: Type[LtConverter]) -> Callable[[_CT, _CT], bool]: def comparator(op: Callable[[LtConverter, LtConverter], bool]) -> Callable[[_CT, _CT], bool]: def method(self: _CT, other: _CT) -> bool: return op(conv(self), conv(other)) # conv(self)触发类型错误 return method return comparator(lambda x, y: x < y)
报错信息:
main.py:18: error: Argument 1 to "LtConverter" has incompatible type "_CT"; expected "_CT" [arg-type]
核心困惑:
- 为何
_CT会被判定为与自身类型不兼容? - 为什么
conv(self)报错,而conv(other)无报错?
错误原因
- 类型变量未与协议绑定:顶层定义的
_CT没有和LtConverter协议建立关联,mypy会将协议__new__方法中的_CT与函数lt_through里的_CT视为两个独立、无关联的类型变量,因此出现看似矛盾的“_CT与_CT不兼容”提示。 - self参数的类型检查特殊性:mypy对类方法中的
self参数有特殊的类型校验逻辑,当类型绑定不清晰时,会优先触发self的类型不匹配报错,而other参数的问题暂时未被触发(本质上两者都存在类型绑定漏洞)。
解决方法
通过Generic让LtConverter协议与_CT类型变量关联,明确协议的转换源类型,同时统一类型变量的绑定关系:
from typing import Callable, TypeVar, Type, Protocol, Generic from typing_extensions import Self _CT = TypeVar("_CT") _T = TypeVar("_T") class LtConverter(Protocol[_CT]): # 将_CT作为协议的泛型参数 def __new__(cls: Type[_T], x: _CT) -> _T: ... def __lt__(self, other: Self) -> bool: ... def lt_through(conv: Type[LtConverter[_CT]]) -> Callable[[_CT, _CT], bool]: def comparator(op: Callable[[LtConverter[_CT], LtConverter[_CT]], bool]) -> Callable[[_CT, _CT], bool]: def method(self: _CT, other: _CT) -> bool: return op(conv(self), conv(other)) return method return comparator(lambda x, y: x < y)
关键修改点
- 将
LtConverter改为泛型协议LtConverter[_CT],明确协议依赖的转换源类型,让mypy能正确追踪类型变量的关联关系。 - 在
lt_through的参数和内部类型标注中,使用LtConverter[_CT]替代原始的LtConverter,确保类型变量的绑定完全一致。
修改后,mypy可正确推断所有类型,不会再出现类型不兼容的错误。
内容的提问来源于stack exchange,提问作者chnmasta05
相关产品推荐
相关产品推荐

