如何为包装Foo子类的func函数正确标注类型?
正确标注分支逻辑的函数类型
假设你的基础代码结构如下:
from typing import TypeVar, Generic class Foo: pass class Bar(Generic[T]): def __init__(self, instance: T) -> None: self.instance = instance
你原来的类型标注def func(a: FooT | T) -> Bar[FooT] | T:会让mypy混淆,因为FooT(Foo的子类)本身属于T的潜在范围,mypy无法明确区分两种输入场景对应的返回类型,从而抛出错误。
最优解法:使用函数重载(@overload)
这种方式能明确告诉mypy两种不同的输入-输出对应关系,是处理这类分支逻辑类型标注的标准做法:
from typing import TypeVar, Generic, overload class Foo: pass class Bar(Generic[T]): def __init__(self, instance: T) -> None: self.instance = instance # 定义绑定到Foo子类的TypeVar FooT = TypeVar('FooT', bound=Foo) # 定义用于非Foo子类的通用TypeVar T = TypeVar('T') @overload def func(a: FooT) -> Bar[FooT]: ... @overload def func(a: T) -> T: ... def func(a): if isinstance(a, Foo): return Bar(a) return a
当你传入Foo子类实例时,mypy会自动推断返回类型为Bar[FooT];传入其他类型时,返回类型与输入类型完全一致,不会出现类型错误。
原标注出错的原因
你原来的写法中,FooT | T的类型集合存在重叠(FooT是Foo的子类,而T没有排除Foo类型),导致mypy无法确定输入到底属于哪一个分支,进而无法匹配对应的返回类型。比如当输入是Foo实例时,mypy既可以把它当成FooT也可以当成T,这就造成了类型推断的歧义,最终触发错误。
可选方案:使用泛型+Union(不推荐)
如果你不想用重载,也可以通过更严格的泛型约束来标注,但这种方式的类型推断效果不如重载清晰:
from typing import TypeVar, Generic, Union class Foo: pass class Bar(Generic[T]): def __init__(self, instance: T) -> None: self.instance = instance FooT = TypeVar('FooT', bound=Foo) T = TypeVar('T') def func(a: Union[FooT, T]) -> Union[Bar[FooT], T]: if isinstance(a, Foo): return Bar(a) return a
这种写法虽然能通过mypy检查,但当你调用func时,返回类型会被推断为Union类型,需要额外的类型断言才能使用Bar的属性,因此重载方案更优。
内容的提问来源于stack exchange,提问作者ignoring_gravity
相关产品推荐
相关产品推荐

