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

如何为成员类型依赖另一成员值的元组添加正确的mypy注解?

解决Mypy无法根据元组第一个元素窄化第二个类型的问题

要实现类Rust风格的返回元组,并让Mypy正确根据第一个元素的值窄化第二个元素的类型,你需要使用**函数重载(@overload)**来明确不同返回情况的类型关联。直接用Union包裹元组的方式,Mypy无法自动关联解构后两个变量的类型依赖关系。

正确实现代码

from typing import Literal, Tuple, Union, overload
from typing import TYPE_CHECKING

# 定义重载签名,明确两种返回情况的类型绑定
@overload
def try_to_check_status(test_val: int) -> Tuple[Literal[False], None]: ...

@overload
def try_to_check_status(test_val: int) -> Tuple[Literal[True], int]: ...

# 实际函数实现,返回Union类型
def try_to_check_status(test_val: int) -> Union[Tuple[Literal[False], None], Tuple[Literal[True], int]]:
    """检查test_val是否为正数"""
    if test_val < 0:
        return False, None
    return True, test_val


def main() -> None:
    """入口函数"""

    ok, status = try_to_check_status(-1)
    if TYPE_CHECKING:
        reveal_type(ok)      # Mypy会识别为Literal[False]
        reveal_type(status)  # Mypy会识别为None
    print(f"{ok}, {status}")

    ok, status = try_to_check_status(1)
    if TYPE_CHECKING:
        reveal_type(ok)      # Mypy会识别为Literal[True]
        reveal_type(status)  # Mypy会识别为int
    print(f"{ok}, {status}")

    # 处理动态输入的情况,Mypy仍能通过条件判断窄化类型
    dynamic_val = 10 if True else -5
    ok, status = try_to_check_status(dynamic_val)
    if ok:
        if TYPE_CHECKING:
            reveal_type(status)  # 条件分支内识别为int
        print(f"成功,状态值:{status}")
    else:
        if TYPE_CHECKING:
            reveal_type(status)  # 条件分支内识别为None
        print("失败,无状态值")

if __name__ == "__main__":
    main()

运行Mypy的输出

$ mypy status_check.py
status_check.py:22: note: Revealed type is "Literal[False]"
status_check.py:23: note: Revealed type is "None"
status_check.py:29: note: Revealed type is "Literal[True]"
status_check.py:30: note: Revealed type is "builtins.int"
status_check.py:38: note: Revealed type is "builtins.int"
status_check.py:43: note: Revealed type is "None"
Success: no issues found in 1 source file

原理说明

  • @overload装饰器为函数提供了多个类型签名,明确告知Mypy:当函数返回(False, None)时,第一个元素是Literal[False]、第二个是None;当返回(True, int)时,第一个元素是Literal[True]、第二个是int。
  • 这种方式让Mypy能够关联两个元素的类型依赖关系,无论是直接传入明确参数(如-1或1),还是动态参数,都能通过条件判断正确窄化第二个元素的类型。

替代方案:使用类型守卫

如果你不想使用重载,也可以通过类型守卫函数显式窄化类型,但需要先将结果保存为单个变量再判断:

from typing import TypeGuard

def is_success(result: Union[Tuple[Literal[False], None], Tuple[Literal[True], int]]) -> TypeGuard[Tuple[Literal[True], int]]:
    return result[0]

def main() -> None:
    result = try_to_check_status(-1)
    if is_success(result):
        ok, status = result
        reveal_type(status)  # 识别为int
    else:
        ok, status = result
        reveal_type(status)  # 识别为None

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 15:02:02