如何将对象链式调用的方法作为参数传入缓存函数,避免预执行fetch()触发数据库查询
缓存查询函数改造方案
核心逻辑约束
链式调用链路里的fetch()方法一旦执行就会触发数据库查询,因此必须保证所有带数据库查询逻辑的调用,都只在缓存未命中时才被触发,绝对不能在传参阶段提前执行,否则会完全失效缓存优化的意义。
方案1:通用回调适配(零侵入,最快落地)
直接保持缓存函数“接收无参可调用对象”的通用签名,将原本的链式查询逻辑用惰性包装传递,完全不修改原有对象的代码:
def get_ref_from_cache_or_db(data_source, key, duration=60): cached_data = get_routes_from_cache(key=key) if cached_data: return json.loads(cached_data) # 仅在缓存未命中时执行传入的数据源回调,触发查库和序列化 result = data_source() set_routes_to_cache(key=key, duration=duration, value=json.dumps(result)) return result
调用时用lambda包装链式逻辑,lambda不会在定义阶段执行内部的fetch()调用,完全符合延迟触发的要求:
# 获取to_dict格式的缓存结果 dict_result = get_ref_from_cache_or_db( lambda: obj.fetch().to_dict(), key="obj_to_dict_cache_key", duration=60 ) # 获取to_other_dict格式的缓存结果 other_dict_result = get_ref_from_cache_or_db( lambda: obj.fetch().to_other_dict(), key="obj_to_other_dict_cache_key", duration=60 )
注意:不同序列化格式的结果必须使用独立的缓存key,避免缓存值和读取格式不匹配的问题
方案2:惰性链式代理(匹配预期调用形式)
如果不想在每次调用时写lambda,可以实现一个极简的惰性链式代理,把链式调用的步骤全部记录下来,直到真正需要取数时才从头执行整个链路,就能写出预期的链式方法传参形式:
class LazyChainProxy: def __init__(self, raw_target): self._target = raw_target self._step_list = [] def __getattr__(self, method_name): # 拦截方法访问,只记录调用步骤,不实际执行 target_attr = getattr(self._target, method_name) if callable(target_attr): self._step_list.append( lambda current_obj: getattr(current_obj, method_name)() ) return self return target_attr def __call__(self): # 缓存未命中真正需要取数时,才按顺序执行所有记录的步骤 current = self._target for step in self._step_list: current = step(current) return current
调用时不需要写lambda,链式传参即可完成惰性绑定,全程不会提前触发数据库查询:
# 初始化对象的惰性代理 lazy_obj = LazyChainProxy(obj) # 直接传链式方法引用,完全符合预期的调用形式 dict_result = get_ref_from_cache_or_db( lazy_obj.fetch().to_dict, key="obj_to_dict_cache_key" ) other_dict_result = get_ref_from_cache_or_db( lazy_obj.fetch().to_other_dict, key="obj_to_other_dict_cache_key" )
如果链路里的方法需要传参,只需要在__getattr__拦截逻辑里把参数和方法名一起存到步骤列表中即可,核心执行逻辑不需要改动。
内容的提问来源于stack exchange,提问作者Mehdi Khlifi
相关产品推荐
相关产品推荐

