如何延迟传入Python类__init__的表达式求值至__call__调用时
实现方案
核心思路是让重载的逻辑运算符不直接返回布尔值结果,而是返回封装了运算关系的可调用对象,把实际求值动作推迟到调用阶段传入运行时参数时再执行。
具体实现步骤
1. 定义逻辑组合节点
先实现一个通用的组合节点类,用来保存运算符两侧的操作数和运算类型,统一实现延迟调用接口:
from typing import Callable, Any class _AuthOpNode: def __init__(self, left: Callable, op_type: str, right: Callable): self.left = left self.op_type = op_type self.right = right def __call__(self, *runtime_args, **runtime_kwargs) -> bool: left_result = self.left(*runtime_args, **runtime_kwargs) # 保留原生逻辑运算符的短路特性 if self.op_type == "or": return left_result or self.right(*runtime_args, **runtime_kwargs) if self.op_type == "and": return left_result and self.right(*runtime_args, **runtime_kwargs) raise ValueError(f"不支持的运算类型: {self.op_type}")
2. 改造Auth基类
把原来直接执行权限判断的运算符重载改成返回组合节点,同时让Auth实例本身和组合节点保持一致的可调用接口:
class Auth: def __init__(self, config_val: str): self.config_val = config_val def has_perm(self, runtime_arg: Any) -> bool: raise NotImplementedError("具体子类实现实际权限判断逻辑") def __call__(self, *runtime_args, **runtime_kwargs) -> bool: return self.has_perm(*runtime_args, **runtime_kwargs) def __or__(self, other: Callable) -> _AuthOpNode: return _AuthOpNode(self, "or", other) def __and__(self, other: Callable) -> _AuthOpNode: return _AuthOpNode(self, "and", other)
3. 实现IsAllowed类
初始化阶段只保存传入的表达式对象,不做任何求值,等到__call__执行时再传入运行时参数触发实际判断:
class IsAllowed: def __init__(self, classes: list[Callable]): self.check_list = classes def __call__(self, runtime_arg: Any) -> bool: # 此处可根据业务需求调整判断逻辑,示例为列表内任意规则通过即返回True for check in self.check_list: if check(runtime_arg): return True return False
使用效果
完全匹配期望的调用接口,初始化阶段不会执行任何权限校验逻辑:
# 实现具体的校验子类 class Auth1(Auth): def has_perm(self, runtime_arg): print(f"Auth1执行校验,配置值:{self.config_val},运行时参数:{runtime_arg}") return runtime_arg.get("allow_auth1", False) class Auth2(Auth): def has_perm(self, runtime_arg): print(f"Auth2执行校验,配置值:{self.config_val},运行时参数:{runtime_arg}") return runtime_arg.get("allow_auth2", False) # 初始化时仅构建表达式树,无实际校验动作 permission_checker = IsAllowed(classes=[Auth1('some_value') | Auth2('some_other_value')]) # 调用阶段传入运行时参数才会执行实际校验,自动保留短路逻辑 final_result = permission_checker({"allow_auth1": True}) # 输出:Auth1执行校验,配置值:some_value,运行时参数:{'allow_auth1': True} # final_result值为True,且不会触发Auth2的校验
扩展说明
- 该方案支持任意层级的逻辑嵌套,比如
Auth1('a') | Auth2('b') & Auth3('c')会自动生成嵌套的运算节点,调用时递归求值即可 - 如果需要支持非运算,只要在Auth类中添加
__invert__方法,返回对应取反逻辑的运算节点即可 - 所有逻辑判断保留原生运算符的短路特性,不会执行不必要的校验逻辑
内容的提问来源于stack exchange,提问作者Amir Afianian
相关产品推荐
相关产品推荐

