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

使用functools.lru_cache忽略字典参数时缓存未生效问题排查

问题定位与解决方案

你的缓存未生效的核心问题出在**BlackBox类未正确实现哈希(__hash__)和相等性(__eq__)方法**,导致即使字典内容相同,每次包装生成的BlackBox实例仍被视为不同对象,lru_cache无法识别为同一缓存键。

具体问题分析

Python默认的__hash__方法基于对象的内存地址(身份)实现,而非对象内容。如果你的BlackBox类仅做了简单的字典包装,即使两个实例的字典内容完全一致,它们的哈希值也会不同,lru_cache会判定为不同参数,导致每次调用都重新执行函数。

修复步骤

1. 修正BlackBox类的哈希与相等性实现

让BlackBox的哈希和相等性基于字典的内容而非实例身份,需要将字典转换为可哈希的有序结构(比如排序后的键值对元组):

class BlackBox:
    def __init__(self, obj):
        self.obj = obj
        # 将字典转换为可哈希的有序结构,确保内容相同的字典生成一致的哈希基础
        if isinstance(obj, dict):
            self._hashable = tuple(sorted(obj.items()))
        else:
            self._hashable = obj

    def __hash__(self):
        # 基于转换后的可哈希结构计算哈希值
        return hash(self._hashable)

    def __eq__(self, other):
        # 仅与同类型实例比较,且比较内容而非身份
        if not isinstance(other, BlackBox):
            return False
        return self._hashable == other._hashable

2. 优化装饰器的参数处理逻辑

确保装饰器先将字典参数替换为BlackBox实例,再传递给lru_cache装饰的内部函数,同时保留原函数的调用参数:

import functools

def lru_cache_ignore(maxsize=None):
    def decorator(func):
        # 内部缓存函数,接收包装后的可哈希参数
        @functools.lru_cache(maxsize=maxsize)
        def cached_func(*processed_args, **processed_kwargs):
            # 将BlackBox实例还原为原始字典,传递给原函数
            original_args = tuple(arg.obj if isinstance(arg, BlackBox) else arg for arg in processed_args)
            original_kwargs = {k: v.obj if isinstance(v, BlackBox) else v for k, v in processed_kwargs.items()}
            return func(*original_args, **original_kwargs)

        # 外部包装函数,处理原始参数
        def wrapped(*args, **kwargs):
            # 将所有字典参数替换为BlackBox实例
            processed_args = tuple(BlackBox(arg) if isinstance(arg, dict) else arg for arg in args)
            processed_kwargs = {k: BlackBox(v) if isinstance(v, dict) else v for k, v in kwargs.items()}
            return cached_func(*processed_args, **processed_kwargs)

        # 复制缓存操作方法到包装函数,方便用户清理/查看缓存
        wrapped.cache_clear = cached_func.cache_clear
        wrapped.cache_info = cached_func.cache_info
        # 保留原函数的元信息(如函数名、文档字符串)
        return functools.wraps(func)(wrapped)
    return decorator

测试验证

使用修复后的代码,输入相同内容的字典时,lru_cache会基于BlackBox的内容哈希识别为同一参数,缓存将正常生效:

call_count = 0

@lru_cache_ignore()
def test_func(a, config):
    global call_count
    call_count += 1
    return a + sum(config.values())

def test_cache_hit():
    global call_count
    call_count = 0
    # 第一次调用
    assert test_func(1, {"x": 2, "y": 3}) == 6
    assert call_count == 1
    # 第二次调用(字典键顺序不同,但内容一致)
    assert test_func(1, {"y": 3, "x": 2}) == 6
    assert call_count == 1  # 缓存命中,调用次数未增加

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 18:35:16