Mypy对方法作用域内的泛型self不做类型检查,调用者作用域则检查
泛型self在Mypy中的类型检查行为疑问
我有一个可生成自身实例的类,部分实例需满足特定类型才能通过外部函数构造。为避免直接标注self类型,我采用了泛型self(遵循Mypy官方文档及PEP 673示例),但发现一个奇怪现象:
- Mypy能识别self的正确类型,但不检查方法作用域内self的类型正确性(比如把self传给期望不同特化类型的函数时不报错)
- 但在调用者作用域(用对象名而非self)或显式标注self类型时,Mypy会正常执行类型检查
以下是测试代码:
from typing import TypeVar, Generic, Type T = TypeVar("T", covariant=True) TShape = TypeVar("TShape", bound="Shape") class Shape(Generic[T]): def __init__(self, scale: T) -> None: self.scale = scale def make_circle(self: Shape[str], radius: float) -> Circle: return Circle(self, radius) def make_circle2(self: TShape, radius: float) -> Circle: return Circle(self, radius) def make_circle3(self: Shape[object], radius: float) -> Circle: # 测试Mypy是否会在方法作用域内抛出类型错误 # error: Argument 1 to "Circle" has incompatible type "Shape[object]"; expected "Shape[str]" [arg-type] return Circle(self, radius) def get_self(self: TShape) -> Type[TShape]: return type(self) class Circle: def __init__(self, shape: Shape[str], radius: float): self.radius = radius self.scale = float(shape.scale) # 创建Shape[int]实例 a = Shape(5) # __main__.Shape[builtins.int] # 这是我们预期的类型错误(为什么是'misc'而非'arg-type'?) b = a.make_circle(5) # error: Invalid self argument "Shape[int]" to attribute function "make_circle" with type "Callable[[Shape[str], float], Circle]" [misc] # 预期这里会和上面报同样的错误,但实际没有 b2 = a.make_circle2(5) # ok: __main__.Circle # 验证Mypy识别出TShape是Shape[int],但允许Circle(self, radius)通过 c = a.get_self() # Type[__main__.Shape[builtins.int]] # 用对象'a'而非self调用时,会报类似的类型错误 d = Circle(a, 5) # error: Argument 1 to "Circle" has incompatible type "Shape[int]"; expected "Shape[str]" [arg-type] # 确认Shape[str]的情况一切正常 a2 = Shape("5") # __main__.Shape[builtins.str] a2.make_circle(5) # ok a2.make_circle2(5) # ok Circle(a2, 5) # ok a.make_circle3(5) reveal_locals()
现象原因解析
这是因为Mypy对泛型self参数的类型处理逻辑和显式标注的self类型不同:
- 显式标注self类型(如
make_circle):Mypy会在调用时直接校验实例类型是否匹配标注的Shape[str],不匹配就报错,这是最直接的类型校验。 - 泛型self参数(如
make_circle2中的TShape):Mypy会尝试推断泛型参数的特化类型,让方法内部的代码能够通过类型检查。在make_circle2中,Mypy会假设TShape是同时满足bound="Shape"和Circle.__init__要求的类型——也就是它会隐式认为TShape兼容Shape[str],从而跳过了方法内部的类型校验。 - 方法作用域内的self校验差异:当你用泛型self时,Mypy把
self看作是泛型参数的实例,会优先保证方法内部代码的类型兼容性,而不会主动校验它是否符合外部函数(如Circle.__init__)的类型要求,除非泛型参数有明确的约束。而TShape的bound="Shape"只是限制它是Shape的子类或实例,没有约束到Shape[str],所以Mypy不会报错。
另外,make_circle报错类型是misc而非arg-type,是因为这属于self参数不匹配方法签名的特殊场景,Mypy把这类错误归类到misc类别,而普通函数参数不匹配才会报arg-type。
内容的提问来源于stack exchange,提问作者TCM
相关产品推荐
相关产品推荐

