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

如何结合抽象基类使用typing.assert_never()实现类型全覆盖检查?

解决抽象基类子类穷尽检查的mypy提示问题

问题背景

你定义了带@abstractmethod、以ABCMeta为元类的抽象基类ParserNode,以及StartTag、RawText等具体子类。编写函数doTheThing接收ParserNode类型参数,希望新增ParserNode子类但未添加对应处理逻辑时,mypy能自动报错。

最初的写法因为mypy默认认为存在未处理的ParserNode抽象类实例(尽管实际上无法实例化),导致assert_never无法正常触发子类缺失的提示;手动维护子类联合类型的方法可行,但需要重复更新类型列表,不够高效。

最优解决方案:使用mypy密封类(Sealed Classes)

从mypy 0.981版本开始,支持密封类特性,能让mypy自动识别抽象基类的所有子类,无需手动维护联合类型,即可实现穷尽检查的报错提示。

步骤1:标记抽象基类为密封类

用@typing.sealed装饰抽象基类,告诉mypy:该类的所有子类都在当前模块(或指定模块)中,不会有外部新增的子类。

from abc import ABC, abstractmethod
from typing import sealed

@sealed
class ParserNode(ABC):
    @abstractmethod
    def some_abstract_method(self) -> None:
        pass

class StartTag(ParserNode):
    def some_abstract_method(self) -> None:
        # 实现逻辑
        pass

class RawText(ParserNode):
    def some_abstract_method(self) -> None:
        # 实现逻辑
        pass

步骤2:编写穷尽检查逻辑

直接使用原来的isinstance分支+assert_never即可,mypy会自动识别所有子类是否都被处理:

from typing import assert_never

def doTheThing(node: ParserNode) -> None:
    if isinstance(node, StartTag):
        # 处理StartTag逻辑
        ...
    elif isinstance(node, RawText):
        # 处理RawText逻辑
        ...
    else:
        assert_never(node)

效果

当新增ParserNode子类(比如EndTag)但未在doTheThing中添加对应分支时,mypy会直接在assert_never处报错,提示EndTag类型未被处理,同时不会再提示未处理ParserNode抽象类的问题。

兼容旧版本mypy的替代方案

如果无法升级mypy到支持密封类的版本,可以通过简化联合类型维护的方式减少重复工作:

from abc import ABC, abstractmethod
from typing import TypeAlias, assert_never, cast, TYPE_CHECKING

class ParserNode(ABC):
    @abstractmethod
    def some_abstract_method(self) -> None:
        pass

class StartTag(ParserNode):
    def some_abstract_method(self) -> None:
        pass

class RawText(ParserNode):
    def some_abstract_method(self) -> None:
        pass

# 仅在类型检查时定义联合类型,集中维护子类列表
if TYPE_CHECKING:
    ParserNodeSubs: TypeAlias = StartTag | RawText

def doTheThing(node: ParserNode) -> None:
    node = cast(ParserNodeSubs, node)
    if isinstance(node, StartTag):
        ...
    elif isinstance(node, RawText):
        ...
    else:
        assert_never(node)

这种方式将子类列表集中在一处,新增子类时只需更新ParserNodeSubs的定义,比分散维护更高效。

内容的提问来源于stack exchange,提问作者Tab Atkins-Bittner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 12:18:15