mypy报错__iadd__与__add__签名不兼容,求排查方案
解决__iadd__与__add__签名不兼容的mypy报错问题
编写带维度类型检查的向量与矩阵代码时,在为__add__和__iadd__方法添加类型检查时遇到mypy报错:Signatures of "__iadd__" and "__add__" are incompatible,但两个方法的重载签名看似完全一致。
问题代码示例
from __future__ import annotations from typing import ( Generic, Literal as L, TypeVar, overload, assert_type ) _D1 = TypeVar("_D1") _D2 = TypeVar("_D2") _D3 = TypeVar("_D3") # TypeVarTuple is an experimental feature; this is a work-around class Shape: """Class that works as a tag to indicate that we are specifying a shape.""" class Shape1D(Shape, Generic[_D1]): pass class Shape2D(Shape, Generic[_D1,_D2]): pass _Shape = TypeVar("_Shape", bound=Shape) Scalar = int | float class Array(Generic[_Shape]): @overload # Adding with the same shape def __add__(self: Array[_Shape], other: Array[_Shape]) -> Array[_Shape]: return Any # type: ignore @overload # Adding with a scalar def __add__(self: Array[_Shape], other: Scalar) -> Array[_Shape]: return Any # type: ignore def __add__(self, other) -> Array: return self # Dummy implementation @overload # Adding with the same shape def __iadd__(self: Array[_Shape], other: Array[_Shape]) -> Array[_Shape]: return Any # type: ignore @overload # Adding with a scalar def __iadd__(self: Array[_Shape], other: Scalar) -> Array[_Shape]: return Any # type: ignore def __iadd__(self, other) -> Array: return self # Dummy implementation # Adding with a scalar def __radd__(self: Array[_Shape], other: Scalar) -> Array[_Shape]: return Any # type: ignore A = Array[Shape2D[L[3],L[4]]]() reveal_type(A + 1.0) ; assert_type(A + 1.0, Array[Shape2D[L[3],L[4]]]) reveal_type(1.0 + A) ; assert_type(1.0 + A, Array[Shape2D[L[3],L[4]]]) reveal_type(A + A) ; assert_type(A + A, Array[Shape2D[L[3],L[4]]]) A += 1.0 A += A
报错原因
mypy对就地操作符(如__iadd__)的类型规则有特殊要求:根据PEP 484规范,就地操作符的返回值应该严格匹配调用对象的自身类型。你的代码中,__iadd__的实现方法返回类型注解为Array(无泛型参数的原始类),而重载签名返回的是Array[_Shape],二者的泛型参数不一致,导致mypy判定签名不兼容。
解决方案
修改__iadd__实现方法的返回类型注解,从Array改为Array[_Shape],确保重载签名和实现的返回类型完全匹配。
修正后的代码
from __future__ import annotations from typing import ( Generic, Literal as L, TypeVar, overload, assert_type ) _D1 = TypeVar("_D1") _D2 = TypeVar("_D2") _D3 = TypeVar("_D3") # TypeVarTuple is an experimental feature; this is a work-around class Shape: """Class that works as a tag to indicate that we are specifying a shape.""" class Shape1D(Shape, Generic[_D1]): pass class Shape2D(Shape, Generic[_D1,_D2]): pass _Shape = TypeVar("_Shape", bound=Shape) Scalar = int | float class Array(Generic[_Shape]): @overload # Adding with the same shape def __add__(self: Array[_Shape], other: Array[_Shape]) -> Array[_Shape]: return Any # type: ignore @overload # Adding with a scalar def __add__(self: Array[_Shape], other: Scalar) -> Array[_Shape]: return Any # type: ignore def __add__(self, other) -> Array[_Shape]: return self # Dummy implementation @overload # Adding with the same shape def __iadd__(self: Array[_Shape], other: Array[_Shape]) -> Array[_Shape]: return Any # type: ignore @overload # Adding with a scalar def __iadd__(self: Array[_Shape], other: Scalar) -> Array[_Shape]: return Any # type: ignore def __iadd__(self, other) -> Array[_Shape]: return self # Dummy implementation # Adding with a scalar def __radd__(self: Array[_Shape], other: Scalar) -> Array[_Shape]: return Any # type: ignore A = Array[Shape2D[L[3],L[4]]]() reveal_type(A + 1.0) ; assert_type(A + 1.0, Array[Shape2D[L[3],L[4]]]) reveal_type(1.0 + A) ; assert_type(1.0 + A, Array[Shape2D[L[3],L[4]]]) reveal_type(A + A) ; assert_type(A + A, Array[Shape2D[L[3],L[4]]]) A += 1.0 A += A
验证效果
修正后,mypy将不再报签名不兼容的错误,所有类型断言和reveal_type的结果也会符合预期。
内容的提问来源于stack exchange,提问作者Thomas Mailund
相关产品推荐
相关产品推荐

