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

如何为包装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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 12:59:56