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

使用Union标注返回两种元组的Python函数,mypy报错如何解决?

问题:Union类型下元组解包触发mypy类型检查错误

我在维护遗留代码时,修改了一个函数,让它可以返回两种元组:要么是原有的二元组,要么是新增的三元组。示例代码如下:

#!/usr/bin/python
from typing import Union

def func(x):
    # type: (bool) -> Union[tuple[str, int], tuple[str, str, int]]
    if x:
        return "one", "two", 0
    return "one", 0

one, two = func(False)
print(f"{one} {two}")
one, two, three = func(True)
print(f"{one} {two} {three}")

代码运行结果符合预期:

$ ./tt.py
one 0
one two 0

但用mypy检测时出现以下错误:

$ mypy tt.py
tt.py:10: error: Too many values to unpack (2 expected, 3 provided)  [misc]
tt.py:12: error: Need more than 2 values to unpack (3 expected)  [misc]
Found 2 errors in 1 file (checked 1 source file)

想问这是我遗漏了写法/配置,还是mypy本身的问题?


解决方案

这不是mypy的问题,而是mypy无法自动根据传入的参数值推断函数返回的具体元组类型,必须显式提供类型提示。以下是几种可行的处理方式:

方式1:使用类型断言

在解包前,通过类型断言明确指定返回值的元组类型,让mypy识别当前分支的返回结构:

from typing import Tuple

# 处理二元组返回场景
result = func(False)  # type: Tuple[str, int]
one, two = result
print(f"{one} {two}")

# 处理三元组返回场景
result = func(True)  # type: Tuple[str, str, int]
one, two, three = result
print(f"{one} {two} {three}")

也可以用运行时断言辅助类型推断(同时增强代码健壮性):

# 二元组场景
result = func(False)
assert isinstance(result, tuple) and len(result) == 2
one, two = result

# 三元组场景
result = func(True)
assert isinstance(result, tuple) and len(result) == 3
one, two, three = result

方式2:使用函数重载

通过@overload装饰器为函数定义不同参数对应的返回类型,让mypy能根据传入的参数字面量值,精准匹配返回类型:

from typing import Tuple, overload
from typing_extensions import Literal  # Python 3.8+ 可直接从typing导入

@overload
def func(x: Literal[True]) -> Tuple[str, str, int]:
    ...

@overload
def func(x: Literal[False]) -> Tuple[str, int]:
    ...

def func(x):
    if x:
        return "one", "two", 0
    return "one", 0

# 此时mypy能正确推断两种调用的返回类型
one, two = func(False)
one, two, three = func(True)

这种方式最符合类型推断的最佳实践,能让mypy完全理解不同参数对应的返回结构,同时代码可读性更强。

方式3:避免Union元组返回(适合重构场景)

如果遗留代码重构空间允许,建议拆分函数或改用类/数据类返回结果,从根源上避免Union元组的歧义问题,比如:

from dataclasses import dataclass

@dataclass
class ResultBase:
    one: str
    two: int

@dataclass
class ExtendedResult(ResultBase):
    three: str

def func(x):
    if x:
        return ExtendedResult("one", 0, "two")
    return ResultBase("one", 0)

# 调用时通过类型判断处理
result = func(False)
print(f"{result.one} {result.two}")

result = func(True)
print(f"{result.one} {result.two} {result.three}")

这种方式虽然改动较大,但能彻底消除类型歧义,更利于长期维护。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 02:34:56