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

重写__add__方法时Mypy提示与父类型不兼容的问题咨询

解决子类重载__add__方法的Mypy类型兼容问题

原始代码

class Person:
    number: int

    def __init__(self) -> None:
        self.number = 0

    def __add__(self, other: 'Person') -> 'Person':
        self.number = self.number + other.number
        return self


class Dan(Person):
    def __init__(self) -> None:
        super().__init__()

    def __add__(self, other: 'Dan') -> 'Dan':
        self.number = self.number + other.number + 1
        return self

Mypy错误输出

test.py:15: error: Argument 1 of "add" is incompatible with supertype "Person"; supertype defines the argument type as "Person" [override]
test.py:15: note: This violates the Liskov substitution principle
test.py:15: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides

问题说明

我认为子类型Dan比Person更具体,不应违反里氏替换原则。最初尝试创建绑定Person的TypeVar person_co = TypeVar("person_co", bound=Person),但因Person未定义而失败;又尝试将Dan的__add__方法参数和返回类型改为Person,Mypy不再报错,但我认为这并非正确做法,想寻求正确的解决方案。

正确解决方案

方法一:使用Python 3.11+的Self类型(推荐)

直接利用标准库的Self类型标注方法的参数和返回值,让类型系统自动推导子类的自引用类型:

from typing import Self

class Person:
    number: int

    def __init__(self) -> None:
        self.number = 0

    def __add__(self: Self, other: Self) -> Self:
        self.number += other.number
        return self


class Dan(Person):
    def __add__(self: Self, other: Self) -> Self:
        self.number += other.number + 1
        return self

方法二:使用泛型TypeVar(兼容旧Python版本)

如果使用Python 3.10及以下版本,可以通过定义自引用TypeVar来实现:

from typing import TypeVar

# 用字符串形式绑定Person,避免未定义问题
T = TypeVar('T', bound='Person')

class Person:
    number: int

    def __init__(self) -> None:
        self.number = 0

    def __add__(self: T, other: T) -> T:
        self.number += other.number
        return self


class Dan(Person):
    def __add__(self: 'Dan', other: 'Dan') -> 'Dan':
        self.number += other.number + 1
        return self

原理说明

原代码违反里氏替换原则的核心原因:父类Person的__add__接受任意Person类型实例,但子类Dan的__add__仅接受Dan类型。当把Dan当作Person使用时,传入普通Person实例会触发类型错误,破坏了子类可替换父类的要求。

使用自引用类型后,__add__方法被约束为仅接受与自身同类型的实例:

  • 调用Person()的__add__时,必须传入Person实例
  • 调用Dan()的__add__时,必须传入Dan实例
    同时,Dan作为Person的子类,在任何需要Person的场景中,其__add__方法也能遵循父类的类型约束,完全符合里氏替换原则。

内容的提问来源于stack exchange,提问作者Shane Heldsinger

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 03:21:06