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

继承类重载不同参数构造函数引发的Mypy类型错误解决方法

解决Mypy严格模式下的类构造函数类型错误

当以下代码在Mypy严格模式下运行时,会触发类型错误:

import abc

class ABCParent(metaclass=abc.ABCMeta):
    def __init__(self, a: str, sibling_type: type[ABCParent]) -> None:
        self.a = a
        self._sibling_type = sibling_type
    def new_sibling(self, a: str) -> ABCParent:
        return self._sibling_type(a)

class ChildA(ABCParent):
    def __init__(self, a: str) -> None:
        super().__init__(a, ChildB)

class ChildB(ABCParent):
    def __init__(self, a: str) -> None:
        super().__init__(a, ChildA)

错误信息如下:

src/demo_problem.py: note: In member "new_sibling" of class "ABCParent":
src/demo_problem.py:10:16:10:36: error: Missing positional argument "sibling_type" in call to "ABCParent"  [call-arg]
            return self._sibling_type(a)

错误逻辑合理:ABCParent的构造函数要求传入sibling_type参数,但实际存储在self._sibling_type的是重写了构造函数、仅需一个参数的子类。

此前用typing.Union子类的方式存在诸多问题:

  • 需定义无实际业务用途的类型别名TConcreteChild
  • 新增子类时必须手动更新该别名的定义
  • 不得不使用字符串形式的前向引用,无法使用ChildA | ChildB这类新语法

更简洁的实现方案

方案一:用Protocol约束构造函数签名

通过typing.Protocol定义协议,明确规定具体子类的构造函数签名,让Mypy正确识别子类的构造参数要求:

import abc
from typing import Protocol, Type, TypeVar

# 定义协议,约束子类构造函数仅接收一个字符串参数
class ConcreteChildProtocol(Protocol):
    def __init__(self, a: str) -> None: ...

# 泛型变量:绑定ABCParent及其子类
T = TypeVar('T', bound='ABCParent')
# 泛型变量:绑定实现了ConcreteChildProtocol的类
SiblingT = TypeVar('SiblingT', bound=ConcreteChildProtocol)

class ABCParent(metaclass=abc.ABCMeta):
    def __init__(self, a: str, sibling_type: Type[SiblingT]) -> None:
        self.a = a
        self._sibling_type = sibling_type
    
    def new_sibling(self, a: str) -> SiblingT:
        return self._sibling_type(a)

class ChildA(ABCParent['ChildA', 'ChildB']):
    def __init__(self, a: str) -> None:
        super().__init__(a, ChildB)

class ChildB(ABCParent['ChildB', 'ChildA']):
    def __init__(self, a: str) -> None:
        super().__init__(a, ChildA)

该方案优势:

  • 无需维护无意义的类型别名,新增子类仅需在继承时指定泛型参数
  • 支持精确的类型推导,new_sibling的返回类型会被Mypy正确识别为对应子类
  • 字符串形式的泛型参数是Python处理循环依赖的标准写法,避免了前向引用的麻烦

方案二:直接用Callable简化标注

如果不需要精确的子类返回类型,可直接将sibling_type标注为Callable[[str], ABCParent],代码更简洁:

import abc
from typing import Callable

class ABCParent(metaclass=abc.ABCMeta):
    def __init__(self, a: str, sibling_type: Callable[[str], ABCParent]) -> None:
        self.a = a
        self._sibling_type = sibling_type
    
    def new_sibling(self, a: str) -> ABCParent:
        return self._sibling_type(a)

class ChildA(ABCParent):
    def __init__(self, a: str) -> None:
        super().__init__(a, ChildB)

class ChildB(ABCParent):
    def __init__(self, a: str) -> None:
        super().__init__(a, ChildA)

这种写法同样能通过Mypy严格模式检查,适合对返回类型精度要求不高的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 22:18:18