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

使用TypeVar返回子类型时mypy为何报错?

为什么这段泛型代码无法通过mypy检查?

问题代码

from typing import Optional, TypeVar


class A:
    pass


class B(A):
    def __init__(self) -> None:
        print("B")


T = TypeVar("T", bound=A)


def foo(x: T) -> Optional[T]:
    if type(x) is B:
        return B()
    return None

mypy报错信息

toy.py:18:16: error: Incompatible return value type (got "B", expected "Optional[T]")  [return-value]
Found 1 error in 1 file (checked 1 source file)

原因解析

你可能误以为B是T的子类型就可以返回,但mypy对泛型类型的检查是静态且严格的:

  • T是绑定到A的类型变量,这意味着T可以是A的任何子类(包括B本身,或者B的子类,比如如果定义class C(B): ...,T也可以是C)。
  • 当你在分支里返回B()时,mypy无法确定T就是B——即使type(x) is B在运行时成立,静态检查阶段mypy不会将T的类型窄化为B,因为泛型函数的类型签名承诺返回的是和输入参数同类型的T,而不是T的父类。

举个反例:如果有子类C(B),当你调用foo(C())时,T会被推断为C,此时进入type(x) is B分支的情况不会发生,但mypy在检查函数时要考虑所有可能的T绑定场景,它无法保证返回的B()始终符合T的类型要求(比如如果T是C,B()不是C的实例)。

另外,type(x) is B的判断也过于严格,它只匹配精确的B类型,不包括B的子类,这也限制了类型窄化的能力。

解决方案

方案1:使用isinstance+类型转换(cast)

如果确定在x是B实例的场景下,T就是B,可以用isinstance让mypy更好地窄化类型,再用cast明确告诉mypy返回值的类型:

from typing import Optional, TypeVar, cast


class A:
    pass


class B(A):
    def __init__(self) -> None:
        print("B")


T = TypeVar("T", bound=A)


def foo(x: T) -> Optional[T]:
    if isinstance(x, B):
        return cast(T, B())
    return None

这里isinstance(x, B)会让mypy知道x的类型是B,结合cast(T, B()),相当于告诉mypy:在这个分支里,T就是B,所以返回B()是符合要求的。

方案2:调整返回类型(如果业务允许)

如果不需要严格返回和输入同类型的T,可以将返回类型改为Optional[A],这样返回B()就完全合法:

def foo(x: T) -> Optional[A]:
    if type(x) is B:
        return B()
    return None

方案3:限制TypeVar为具体类型

如果foo函数只处理B类型的输入,可以直接将T的约束改为B,或者直接不用泛型:

T = TypeVar("T", bound=B)

def foo(x: T) -> Optional[T]:
    return cast(T, B())

# 或者更简单的非泛型写法:
def foo(x: B) -> Optional[B]:
    return B()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 19:23:10