Python中延迟求值对象与普通值的统一响应问题求解
解决延迟求值对象与显式值行为一致的问题
你之前尝试修改实例的__class__来对齐行为的思路走不通——Python中大多数特殊操作符(比如+、-)是从类层面查找的,而非实例;如果延迟函数返回的类型动态变化,这种方法更是彻底失效。下面是更稳健的解决方案,通过代理模式让延迟求值对象完全模拟其求值结果的行为:
基础实现版本
import random class DelayedEvaluation: def __init__(self, function): assert callable(function), f"Input argument {function} is not callable and can thus not be a delayed evaluation." self._delayed_func = function def _evaluate(self): """执行延迟函数并返回结果,抽离逻辑便于复用""" return self._delayed_func() def __repr__(self): return repr(self._evaluate()) def __str__(self): return str(self._evaluate()) # 代理所有未定义的属性/方法访问 def __getattr__(self, name): evaluated_value = self._evaluate() return getattr(evaluated_value, name) # 实现加法操作(包括正向和反向,比如 1 + delayed 和 delayed + 1) def __add__(self, other): return self._evaluate() + other def __radd__(self, other): return other + self._evaluate() # 实现减法操作 def __sub__(self, other): return self._evaluate() - other def __rsub__(self, other): return other - self._evaluate() # 实现乘法操作 def __mul__(self, other): return self._evaluate() * other def __rmul__(self, other): return other * self._evaluate() # 按需添加其他操作符:比如 __truediv__ 除法、__lt__ 小于比较等
测试代码验证效果:
delayed_evaluation = DelayedEvaluation(lambda: 42) print(delayed_evaluation) # 输出: 42 print(delayed_evaluation + 1) # 输出: 43 print(1 + delayed_evaluation) # 输出: 43 print(delayed_evaluation.bit_length()) # 输出: 6(整数42的二进制位数) for i in range(5): value = random.choice((DelayedEvaluation(lambda: 42), 43)) print(value + 1) # 无论是延迟实例还是显式值,都能正常输出43或44
进阶通用版本(自动生成操作符方法)
如果需要支持大量操作符,手动写每个方法太繁琐,可以用装饰器自动生成所有常见操作符的实现:
import random import operator def add_operator_methods(cls): """自动为类添加常见操作符的代理方法""" # 定义需要支持的操作符及其对应的特殊方法名 operator_map = [ ('add', '__add__', '__radd__'), ('sub', '__sub__', '__rsub__'), ('mul', '__mul__', '__rmul__'), ('truediv', '__truediv__', '__rtruediv__'), ('lt', '__lt__'), ('le', '__le__'), ('eq', '__eq__'), ('ne', '__ne__'), ('gt', '__gt__'), ('ge', '__ge__'), ('mod', '__mod__', '__rmod__'), ] for op_name, *method_names in operator_map: op_func = getattr(operator, op_name) for method_name in method_names: def create_proxy_method(op_func, is_reversed): def method(self, other): evaluated = self._evaluate() if is_reversed: return op_func(other, evaluated) return op_func(evaluated, other) return method # 判断是否是反向操作符(比如 __radd__) is_reversed = method_name.startswith('__r') setattr(cls, method_name, create_proxy_method(op_func, is_reversed)) return cls @add_operator_methods class DelayedEvaluation: def __init__(self, function): assert callable(function), f"Input argument {function} is not callable and can thus not be a delayed evaluation." self._delayed_func = function def _evaluate(self): return self._delayed_func() def __repr__(self): return repr(self._evaluate()) def __str__(self): return str(self._evaluate()) def __getattr__(self, name): evaluated_value = self._evaluate() return getattr(evaluated_value, name)
核心原理说明
_evaluate方法:统一处理延迟函数的执行,避免重复代码,保证每次操作都能获取最新的求值结果(如果你的延迟函数是动态的,比如返回随机数,这一点很重要)。__getattr__方法:当访问实例上不存在的属性或方法时,自动转发给求值后的结果。比如调用delayed.bit_length()时,会先求值得到42,再调用42的bit_length()方法。- 操作符代理:对于
+、-这类特殊操作符,Python会优先查找类定义的特殊方法(比如__add__),所以我们需要显式实现这些方法,将操作转发给求值后的结果。反向操作符(比如__radd__)是为了处理显式值 + 延迟对象的场景。
内容的提问来源于stack exchange,提问作者Adriaan
相关产品推荐
相关产品推荐

