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

Python typing:重写方法返回派生类如何通过mypy类型检查?

解决抽象基类方法返回子类列表的类型检查问题

你定义了一个抽象基类(ABC),其中的方法声明返回list[Base],但子类实现时返回list[Derived](Derived是Base的子类)会被mypy判定为返回类型不兼容,同时返回list[str]的错误实现也触发同样的错误提示——这不符合预期:前者应该通过检查,后者应该报错。

原问题代码

from abc import ABC, abstractmethod
from typing import override


class Base:
    pass


class Derived(Base):
    pass


class Abstract(ABC):
    @abstractmethod
    def foo(self) -> list[Base]: ...


# 预期应该通过检查:返回Derived实例列表(Derived是Base子类)
class ConcreteShouldPass(Abstract):
    @override
    def foo(self) -> list[Derived]:
        return [Derived()]


# 预期应该触发错误:返回str列表,与Base无关
class ConcreteShouldFail(Abstract):
    @override
    def foo(self) -> list[str]:
        return ["foo"]

触发的mypy错误

foo.py:20: error: Return type "list[Derived]" of "foo" incompatible with return type "list[Base]" in supertype "Abstract"  [override]
foo.py:26: error: Return type "list[str]" of "foo" incompatible with return type "list[Base]" in supertype "Abstract"  [override]

问题原因

Python的list泛型是不变的(invariant),也就是说list[Derived]并不是list[Base]的子类型——虽然Derived是Base的子类,但泛型容器的不变性导致这种替换不被允许:你可以向list[Base]中添加任意Base子类实例,但list[Derived]只能接受Derived实例,类型系统不允许这种潜在的类型不安全场景。

解决方案

方案一:使用协变的泛型容器

如果你的场景可以接受返回更宽泛的序列类型(比如只读序列),可以把抽象方法的返回类型改为Sequence[Base]。Sequence是协变的,意味着Sequence[Derived]是Sequence[Base]的子类型,符合类型安全要求:

from abc import ABC, abstractmethod
from typing import override, Sequence


class Base:
    pass


class Derived(Base):
    pass


class Abstract(ABC):
    @abstractmethod
    def foo(self) -> Sequence[Base]: ...


class ConcreteShouldPass(Abstract):
    @override
    def foo(self) -> Sequence[Derived]:
        return [Derived()]  # 列表是Sequence的子类,符合要求


class ConcreteShouldFail(Abstract):
    @override
    def foo(self) -> Sequence[str]:
        return ["foo"]  # mypy会正确触发错误

方案二:用协变TypeVar定义抽象类

如果你必须返回list类型,可以通过定义协变的TypeVar来实现类型兼容:

from abc import ABC, abstractmethod
from typing import override, TypeVar, Generic

# 定义协变TypeVar,绑定到Base及其子类
T = TypeVar('T', bound='Base', covariant=True)


class Base:
    pass


class Derived(Base):
    pass


class Abstract(ABC, Generic[T]):  # 让抽象类继承Generic[T]
    @abstractmethod
    def foo(self) -> list[T]: ...


class ConcreteShouldPass(Abstract[Derived]):
    @override
    def foo(self) -> list[Derived]:
        return [Derived()]  # 类型匹配,通过检查


class ConcreteShouldFail(Abstract[Base]):
    @override
    def foo(self) -> list[str]:
        return ["foo"]  # mypy会正确触发错误

这样修改后,ConcreteShouldPass的实现会通过mypy检查,而ConcreteShouldFail的错误实现依然会被捕获。

内容的提问来源于stack exchange,提问作者Øyvind Evju

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 23:10:01