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

Mypy误判返回Union[A,B]的工厂函数返回Parent的原因及优化方案

问题:Mypy对工厂函数返回类型的误判及优化写法

复现代码

from typing import Union


class Parent:
    def __init__(self) -> None:
        print("In Parent")


class A(Parent):
    def __init__(self) -> None:
        super().__init__()
        print("In A")


class B(Parent):
    def __init__(self) -> None:
        super().__init__()
        print("In B")


def factory(arg: str) -> Union[A, B]:
    mapper = {"A": A, "B": B}

    return mapper[arg]()

Mypy报错信息

error: Incompatible return value type (got "Parent", expected "Union[A, B]")

疑问

这段代码要么在参数非法时报错,要么返回Union中指定的子类实例,为何Mypy会认为返回的是Parent类型?是否有更优的写法?以下分支判断的变体没有报错,但子类较多时更倾向于字典映射的写法:

def factory(arg: str) -> Union[A, B]:
    if arg == "A":
        return A()

    return B()

解答

报错原因

Mypy对字典类型的推导是保守的:当定义mapper = {"A": A, "B": B}时,它会自动将字典值的类型推导为type[Parent]——因为A和B都是Parent的子类,Mypy默认取它们的公共父类作为字典值的统一类型。因此调用mapper[arg]()时,Mypy认为返回的是Parent实例,和函数标注的Union[A, B]返回类型冲突。

优化写法

以下几种方式既能保留字典映射的简洁性,又能让Mypy正确识别类型:

1. 显式标注字典类型

直接给mapper指定精确的类型,告诉Mypy字典的值是A或B的类:

from typing import Union, Dict, Type

# 类定义不变

def factory(arg: str) -> Union[A, B]:
    mapper: Dict[str, Union[Type[A], Type[B]]] = {"A": A, "B": B}
    return mapper[arg]()

2. 用Literal限定输入参数(推荐)

如果factory的入参arg只能是"A"或"B",用Literal明确输入范围后,Mypy能精准推导返回类型:

from typing import Union, Literal

# 类定义不变

def factory(arg: Literal["A", "B"]) -> Union[A, B]:
    mapper = {"A": A, "B": B}
    return mapper[arg]()

3. 用cast强制类型转换

如果不想修改字典类型标注,也可以用cast告知Mypy实际返回类型,但这种方式属于绕过类型检查,仅作为临时方案:

from typing import Union, cast

# 类定义不变

def factory(arg: str) -> Union[A, B]:
    mapper = {"A": A, "B": B}
    return cast(Union[A, B], mapper[arg]())

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 07:09:11