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

派生类调用基类相同重载签名时的MyPy类型检查问题

解决MyPy中派生类调用基类重载构造函数的类型检查问题

MyPy配置

使用MyPy 1.2.0,配置如下:

[tool.mypy]
show_column_numbers   = true
disallow_any_expr     = true
disallow_untyped_defs = true

问题场景

代码中存在类属性类型与另一属性值关联的逻辑:当null为True时,enum允许包含None;当null为False时,enum只能是纯字符串列表或None。相关代码如下:

from typing import List, Literal, overload


class Base:
    null: bool
    enum: List[str | None] | List[str] | None

    @overload
    def __init__(
        self: "Base", null: Literal[True] = ..., enum: List[str | None] | None = ...,
    ) -> None:
        pass

    @overload
    def __init__(
        self: "Base", null: Literal[False] = ..., enum: List[str] | None = ...,
    ) -> None:
        pass

    def __init__(
        self: "Base", null: bool = False, enum: List[str | None] | List[str] | None = None,
    ) -> None:
        self.null = null
        self.enum = enum


class Derived(Base):
    @overload
    def __init__(
        self: "Derived", null: Literal[True] = ..., enum: List[str | None] | None = ...,
    ) -> None:
        pass

    @overload
    def __init__(
        self: "Derived", null: Literal[False] = ..., enum: List[str] | None = ...,
    ) -> None:
        pass

    def __init__(
        self: "Derived", null: bool = False, enum: List[str | None] | List[str] | None = None,
    ) -> None:
        super().__init__(null=null, enum=enum)

MyPy错误信息

执行类型检查时,MyPy抛出如下错误:

overload-with-types.py:43:9: error: No overload variant of "init" of "Base" matches argument types "bool", "Union[List[Optional[str]], List[str], None]" [call-overload]
overload-with-types.py:43:9: note: Possible overload variants:
overload-with-types.py:43:9: note: def init(self, null: Literal[True] = ..., enum: Optional[List[Optional[str]]] = ...) -> None
overload-with-types.py:43:9: note: def init(self, null: Literal[False] = ..., enum: Optional[List[str]] = ...) -> None

当前修复方案

可以通过分支判断结合cast修复错误,但这种方式会随着关联属性数量增加急剧膨胀(n组二元选择会产生2ⁿ个分支):

def __init__(
        self: "Derived", null: bool = False, enum: List[str | None] | List[str] | None = None,
    ) -> None:
        if null:
            super().__init__(null=null, enum=cast(List[str | None] | None, enum))
        else:
            super().__init__(null=null, enum=cast(List[str] | None, enum))

诉求

希望找到Pythonic的解决方案,既能保证类型检查严格性(不放宽MyPy配置),又无需编写大量分支和cast,同时让类型检查器识别基类与派生类的重载签名一致性。


解决方案

方案1:直接继承基类构造函数(优先推荐)

如果派生类不需要新增构造逻辑或参数,完全可以省略派生类的__init__定义,直接继承基类的所有重载和实现。这种方式最简洁,也不会触发类型检查错误:

from typing import List, Literal, overload

class Base:
    null: bool
    enum: List[str | None] | List[str] | None

    @overload
    def __init__(
        self: "Base", null: Literal[True] = ..., enum: List[str | None] | None = ...,
    ) -> None:
        pass

    @overload
    def __init__(
        self: "Base", null: Literal[False] = ..., enum: List[str] | None = ...,
    ) -> None:
        pass

    def __init__(
        self: "Base", null: bool = False, enum: List[str | None] | List[str] | None = None,
    ) -> None:
        self.null = null
        self.enum = enum

class Derived(Base):
    # 无需重新定义__init__,自动继承基类的构造逻辑和类型注解
    pass

方案2:单次cast避免分支

如果派生类需要自定义__init__逻辑,可以通过一次cast将参数转换为基类重载接受的联合类型,无需分支判断。这种方式利用cast告知类型检查器参数符合重载要求,同时保持代码简洁:

from typing import List, Literal, overload, cast, Union

class Base:
    null: bool
    enum: List[str | None] | List[str] | None

    @overload
    def __init__(
        self: "Base", null: Literal[True] = ..., enum: List[str | None] | None = ...,
    ) -> None:
        pass

    @overload
    def __init__(
        self: "Base", null: Literal[False] = ..., enum: List[str] | None = ...,
    ) -> None:
        pass

    def __init__(
        self: "Base", null: bool = False, enum: List[str | None] | List[str] | None = None,
    ) -> None:
        self.null = null
        self.enum = enum

class Derived(Base):
    @overload
    def __init__(
        self: "Derived", null: Literal[True] = ..., enum: List[str | None] | None = ...,
    ) -> None:
        pass

    @overload
    def __init__(
        self: "Derived", null: Literal[False] = ..., enum: List[str] | None = ...,
    ) -> None:
        pass

    def __init__(
        self: "Derived", null: bool = False, enum: List[str | None] | List[str] | None = None,
    ) -> None:
        # 单次cast告知类型检查器参数符合基类重载要求
        super().__init__(
            null=null, 
            enum=cast(Union[List[str | None] | None, List[str] | None], enum)
        )

方案3:泛型关联属性类型(适用于复杂关联场景)

通过Generic和TypeVar绑定关联属性的类型关系,让类型检查器自动推导属性间的约束,从而避免重复重载。注意该方案需要MyPy 1.3+版本支持:

from typing import List, Literal, Generic, TypeVar

# 定义绑定bool的TypeVar
NullType = TypeVar('NullType', bound=bool)

class Base(Generic[NullType]):
    null: NullType
    # 根据NullType的类型约束enum的类型
    enum: List[str | None] | None if NullType is Literal[True] else List[str] | None

    def __init__(
        self: "Base[NullType]", 
        null: NullType = False, 
        enum: List[str | None] | None if NullType is Literal[True] else List[str] | None = None,
    ) -> None:
        self.null = null
        self.enum = enum

class Derived(Base[NullType], Generic[NullType]):
    def __init__(
        self: "Derived[NullType]", 
        null: NullType = False, 
        enum: List[str | None] | None if NullType is Literal[True] else List[str] | None = None,
    ) -> None:
        super().__init__(null=null, enum=enum)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 17:24:53