You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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类型不同:

  1. 显式标注self类型(如make_circle):Mypy会在调用时直接校验实例类型是否匹配标注的Shape[str],不匹配就报错,这是最直接的类型校验。
  2. 泛型self参数(如make_circle2中的TShape):Mypy会尝试推断泛型参数的特化类型,让方法内部的代码能够通过类型检查。在make_circle2中,Mypy会假设TShape是同时满足bound="Shape"和Circle.__init__要求的类型——也就是它会隐式认为TShape兼容Shape[str],从而跳过了方法内部的类型校验。
  3. 方法作用域内的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 02:52:52