如何为与其他属性关联的可选属性做类型检查?
解决方案:使用受歧视联合类型(Discriminated Unions)
你遇到的核心问题是mypy无法识别success布尔值和data非空性之间的关联。最优雅且一劳永逸的解决方式是用受歧视联合类型——将成功和失败的结果拆分为两个不同的类型,用一个共享的标记字段让mypy能自动完成类型窄化。
单字段场景示例
from dataclasses import dataclass from typing import Union @dataclass class SuccessResult: type: str = "success" # 标记字段,用于区分类型 data: int # 成功时字段必然非空 @dataclass class FailureResult: type: str = "failure" # 与SuccessResult做类型区分的标记 def compute(inputs: str) -> Union[SuccessResult, FailureResult]: if inputs.startswith('!'): return FailureResult() return SuccessResult(data=len(inputs)) def check(inputs: str) -> bool: result = compute(inputs) if result.type == "success": # mypy会自动推断result是SuccessResult,data必然为int类型 return result.data > 2 return False assert check('123') assert not check('12') assert not check('!123')
运行mypy不会报错:当判断result.type == "success"时,mypy会自动将result的类型窄化为SuccessResult,自然知道data是确定的int而非Optional[int]。
多字段场景扩展
当存在data_x、data_y、data_z这类多字段时,只需在SuccessResult中定义所有非空字段,FailureResult中无需包含这些无效字段:
from dataclasses import dataclass from typing import Union @dataclass class SuccessResult: type: str = "success" data_x: int data_y: str data_z: float @dataclass class FailureResult: type: str = "failure" def compute(inputs: str) -> Union[SuccessResult, FailureResult]: if inputs.startswith('!'): return FailureResult() # 模拟多字段计算逻辑 return SuccessResult( data_x=len(inputs), data_y=inputs.upper(), data_z=len(inputs) / 2 ) def check(inputs: str) -> bool: result = compute(inputs) if result.type == "success": # mypy能自动推断所有data_*字段都非空 return result.data_x > 2 and len(result.data_y) > 3 return False
这种方式完全不需要手动添加cast,也不用重复检查每个字段的非空性,mypy会自动根据标记字段完成类型推断。
方案优势对比
- 无需每次使用字段都手动
cast,一次定义类型关联,所有场景可复用 - 多字段场景下,避免了冗长的
data_x is not None and data_y is not None...检查逻辑,靠类型本身区分成功/失败状态 - 类型定义更清晰:成功结果的字段必然非空,失败结果无无效字段,消除
Optional带来的类型歧义
内容的提问来源于stack exchange,提问作者a_guest
相关产品推荐
相关产品推荐

