如何为成员类型依赖另一成员值的元组添加正确的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
相关产品推荐
相关产品推荐

