使用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
相关产品推荐
相关产品推荐

