You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用functools.lru_cache时如何将不同传参模式视为同一次调用?

解决方案

最符合Python风格的实现是基于标准库inspect模块对函数参数做归一化处理,封装自定义的缓存装饰器,统一不同传参模式的缓存键。

核心思路

利用inspect.signature获取函数的参数签名,将任意传入的位置参数、关键字参数绑定到签名上,自动对齐参数顺序、合并关键字参数顺序,生成标准化的参数元组作为缓存键,完全规避传参方式差异带来的缓存失效问题。

实现代码

以下是兼容原生lru_cache所有能力的通用装饰器实现:

import functools
import inspect

def normalized_lru_cache(maxsize=128, typed=False):
    def decorator(func):
        # 提前获取目标函数的参数签名
        sig = inspect.signature(func)
        # 用原生lru_cache包裹参数归一化后的内部调用
        @functools.lru_cache(maxsize=maxsize, typed=typed)
        def cached_func(*bound_args):
            return func(*bound_args[:-len(sig.parameters)], **dict(bound_args[-1]))
        
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # 将任意传参形式绑定到函数签名,对齐参数顺序、补全默认值
            bound = sig.bind(*args, **kwargs)
            bound.apply_defaults()
            # 对关键字参数排序后生成统一的缓存键,消除关键字传参顺序的影响
            normalized_args = bound.args + tuple(sorted(bound.kwargs.items()))
            return cached_func(*normalized_args)
        
        # 暴露原生lru_cache的常用方法,和原生使用体验完全一致
        wrapper.cache_info = cached_func.cache_info
        wrapper.cache_clear = cached_func.cache_clear
        return wrapper
    return decorator

测试效果

替换原代码中的lru_cache即可直接生效:

@normalized_lru_cache(maxsize=128)
def test(a, b, *, c, d):
    print(f'Hello from function: ({a}, {b}, {c}, {d})')

test(1, 2, c=42, d='answer')
test(1, 2, d='answer', c=42)
test(b=2, a=1, c=42, d='answer')

运行后仅会输出1次执行结果,后两次调用都会命中缓存,符合预期。

方案优势

  • 完全兼容原生functools.lru_cache的所有参数和工具方法,替换成本为0
  • 无需修改原函数业务逻辑,仅替换装饰器即可生效
  • 完全基于Python标准库实现,无第三方依赖,符合Pythonic设计规范

内容的提问来源于stack exchange,提问作者user13375075

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 19:27:03