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

如何不使用Union解决mypy子类方法返回类型与父类不兼容报错

解决方案

这里提供三种可实现需求的方案,优先推荐泛型基类方案,既可以保留完整类型检查能力,也不会引入额外运行时依赖。

方案1:泛型公共基类(推荐)

首先你需要调整类结构,抽取完全不绑定同步/异步实现的公共基类,同步、异步客户端分别继承基类并绑定对应的返回类型,从设计层面也更合理:同步和异步客户端调用逻辑完全不同,原本就不满足里氏替换原则,不应该直接让异步类继承同步类。

实现代码

公共基类代码(base.py):

from __future__ import annotations
from typing import TypeVar, Generic, TYPE_CHECKING

# 仅类型检查阶段导入依赖,运行时不会执行
if TYPE_CHECKING:
    import requests
    import aiohttp

# 协变类型变量,用于占位返回类型
ResponseT = TypeVar("ResponseT", covariant=True)

class BaseClient(Generic[ResponseT]):
    # 公共方法、公共属性都放在这里实现
    def request(self, url: str) -> ResponseT:
        raise NotImplementedError

同步客户端代码(client.py),完全无异步依赖:

import requests
from .base import BaseClient

class Client(BaseClient[requests.Response]):
    def request(self, url: str) -> requests.Response:
        # 同步请求实现逻辑
        return requests.get(url)

异步客户端代码(async_client.py),所有异步依赖仅在这个模块引入,用户不安装异步额外依赖时不会加载该模块就不会报错:

import aiohttp
from .base import BaseClient

class AsyncClient(BaseClient[aiohttp.ClientResponse]):
    async def request(self, url: str) -> aiohttp.ClientResponse:
        # 异步请求实现逻辑
        async with aiohttp.ClientSession() as session:
            resp = await session.get(url)
            await resp.read()
            return resp

方案2:类型忽略标记(快速修复)

如果你不想调整现有继承结构,可以直接在重写方法上添加类型忽略标记,让mypy跳过本次返回类型兼容性检查:

class AsyncClient(Client):
    # type: ignore[override] 仅忽略重写类型不兼容的检查
    def request(self, url: str) -> aiohttp.ClientResponse:
        ...

该方案缺点是会丢失这部分的重写类型校验,仅适合临时快速修复。

方案3:父类返回类型标注为Any

如果不需要对父类request方法做严格的返回类型校验,可以直接把父类返回类型标注为Any,子类返回任意类型都不会触发报错:

from typing import Any

class Client:
    def request(self, url: str) -> Any:
        ...

该方案缺点是会丢失父类request方法的返回类型检查能力。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 11:06:03