如何优化Python解析器代码结构,减少空值检查并满足代码规范?
解析器结果类的约束与代码优化方案
一、为NodeResult添加字段条件依赖约束
你的需求是让NodeResult的字段满足:
- 若
tokens非空,则node必须有值且error_message为空 - 若
error_message非空,则tokens和node必须为空
可以通过以下几种方式实现:
1. 类方法封装实例创建逻辑(推荐)
通过类方法统一生成成功/失败实例,从源头避免外部构造违反约束的对象:
from dataclasses import dataclass, field from typing import Optional, List, Union # 假设已定义Token、ExpressionNode、TermNode、FactorNode类 @dataclass class NodeResult: was_successful: bool tokens: Optional[List[Token]] = field(default_factory=list) node: Union[ExpressionNode, TermNode, FactorNode, None] = None error_message: str = "" @classmethod def success(cls, tokens: List[Token], node: Union[ExpressionNode, TermNode, FactorNode]) -> 'NodeResult': return cls( was_successful=True, tokens=tokens, node=node, error_message="" ) @classmethod def failure(cls, error_message: str) -> 'NodeResult': return cls( was_successful=False, tokens=[], node=None, error_message=error_message )
外部仅能通过NodeResult.success()或NodeResult.failure()创建实例,确保约束始终被遵守。
2. 运行时校验(补充保障)
在初始化阶段添加校验逻辑,一旦出现违反约束的情况立即抛出异常:
@dataclass class NodeResult: was_successful: bool tokens: Optional[List[Token]] = field(default_factory=list) node: Union[ExpressionNode, TermNode, FactorNode, None] = None error_message: str = "" def __post_init__(self): if self.was_successful: if not self.node or self.error_message: raise ValueError("成功状态下必须存在node且error_message为空") if not isinstance(self.tokens, list): raise ValueError("成功状态下tokens必须为列表类型") else: if self.tokens or self.node: raise ValueError("失败状态下tokens和node必须为空") if not self.error_message: raise ValueError("失败状态下必须提供error_message")
3. 代数数据类型(静态类型友好)
拆分成功/失败状态为不同子类,让类型检查工具能自动识别不同状态的字段:
from dataclasses import dataclass from typing import List, Union @dataclass class BaseResult: pass @dataclass class SuccessResult(BaseResult): tokens: List[Token] node: Union[ExpressionNode, TermNode, FactorNode] @dataclass class FailureResult(BaseResult): error_message: str # 解析函数返回明确的结果类型 def parse_tokens_for_term(tokens: List[Token]) -> Union[SuccessResult, FailureResult]: # 成功返回SuccessResult,失败返回FailureResult ...
二、避免重复的类型检查与None判断
1. 子类化结果类型解决静态检查问题
基于代数数据类型方案,为每个解析结果定义专属子类,明确返回类型:
@dataclass class TermSuccessResult(SuccessResult): node: TermNode @dataclass class ExpressionSuccessResult(SuccessResult): node: ExpressionNode def parse_tokens_for_term(tokens: List[Token]) -> Union[TermSuccessResult, FailureResult]: # 成功时返回TermSuccessResult ... # 使用示例 term_result = parse_tokens_for_term(tokens) if isinstance(term_result, FailureResult): return term_result # 类型检查工具自动识别term_result.node为TermNode,无需额外判断 expression_node = ExpressionNode(term_result.node)
2. 封装辅助函数简化重复逻辑
将失败判断、类型校验逻辑封装到通用函数中,减少代码冗余:
from typing import TypeVar, Generic T = TypeVar('T', ExpressionNode, TermNode, FactorNode) def unwrap_result(result: NodeResult, expected_type: type[T]) -> T: if not result.was_successful: raise ValueError("无法解析失败的结果") if not isinstance(result.node, expected_type): raise TypeError(f"预期类型为{expected_type.__name__},实际为{type(result.node).__name__}") return result.node # 使用示例 term_node_result = parse_tokens_for_term(tokens) if not term_node_result.was_successful: return term_node_result try: term_node = unwrap_result(term_node_result, TermNode) except (ValueError, TypeError) as e: return report_error(unexpected_type=str(type(term_node_result.node))) expression_node = ExpressionNode(term_node)
3. 类型断言提示静态检查工具
通过类型断言告知代码检查工具node的具体类型,无需运行时校验:
from typing import cast term_node_result = parse_tokens_for_term(tokens) if not term_node_result.was_successful: return term_node_result # 用cast明确告知类型检查工具node的类型 term_node = cast(TermNode, term_node_result.node) expression_node = ExpressionNode(term_node)
内容的提问来源于stack exchange,提问作者8SIXSector
相关产品推荐
相关产品推荐

