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

Python中修改LFU Cache:自定义淘汰与恢复逻辑问题排查

解决LFU缓存子类的问题

核心问题排查方向

  • 确认淘汰逻辑是否正确将键值存入__del_freq:如果父类的淘汰方法没被正确重写,或者淘汰时没先获取元素频率就删除,会导致__del_freq无数据。
  • 确认put方法中__del_freq的操作:有没有用正确的键查找,是否漏写了del self.__del_freq[key],或者逻辑顺序搞反(比如先调用父类put再处理__del_freq)。
  • 确认父类方法的调用时机:重写方法时,有没有覆盖必要的父类逻辑,或者遗漏了关键步骤。

修正后的代码示例

假设你用的是标准LFUCache基础实现,子类的正确写法如下:

from collections import defaultdict, OrderedDict

class LFUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = {}  # key: (value, freq)
        self.freq_map = defaultdict(OrderedDict)  # freq: OrderedDict存储对应key
        self.min_freq = 1

    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        val, freq = self.cache[key]
        # 更新频率:移除旧频率分组,加入新频率分组
        del self.freq_map[freq][key]
        if not self.freq_map[freq]:
            del self.freq_map[freq]
            if self.min_freq == freq:
                self.min_freq += 1
        freq += 1
        self.freq_map[freq][key] = None
        self.cache[key] = (val, freq)
        return val

    def put(self, key: int, value: int) -> None:
        if self.capacity == 0:
            return
        if key in self.cache:
            self.cache[key] = (value, self.cache[key][1])
            self.get(key)  # 复用get的频率更新逻辑
            return
        # 缓存满则先淘汰
        if len(self.cache) >= self.capacity:
            self.evict()
        # 新增元素初始频率为1
        self.cache[key] = (value, 1)
        self.freq_map[1][key] = None
        self.min_freq = 1

    def evict(self) -> None:
        # 父类淘汰逻辑:删除最小频率组的第一个key
        evict_key, _ = self.freq_map[self.min_freq].popitem(last=False)
        del self.cache[evict_key]
        if not self.freq_map[self.min_freq]:
            del self.freq_map[self.min_freq]

# 你的自定义LFU子类
class MyLFUCache(LFUCache):
    def __init__(self, capacity: int):
        super().__init__(capacity)
        self.__del_freq = {}  # 存储被淘汰键的历史频率

    def evict(self) -> None:
        # 重写淘汰方法:先记录被淘汰元素的频率,再执行删除
        evict_key, _ = self.freq_map[self.min_freq].popitem(last=False)
        # 淘汰前先获取该元素的频率,存入__del_freq
        self.__del_freq[evict_key] = self.cache[evict_key][1]
        # 执行父类剩余的淘汰逻辑
        del self.cache[evict_key]
        if not self.freq_map[self.min_freq]:
            del self.freq_map[self.min_freq]

    def put(self, key: int, value: int) -> None:
        # 优先处理__del_freq中的历史记录
        if key in self.__del_freq:
            target_freq = self.__del_freq[key]
            # 必须删除__del_freq中的记录,避免重复读取
            del self.__del_freq[key]
            
            # 处理缓存插入逻辑
            if len(self.cache) < self.capacity:
                self.cache[key] = (value, target_freq)
                self.freq_map[target_freq][key] = None
                # 更新最小频率
                if target_freq < self.min_freq:
                    self.min_freq = target_freq
                return
            # 缓存满则先淘汰再插入
            self.evict()
            self.cache[key] = (value, target_freq)
            self.freq_map[target_freq][key] = None
            if target_freq < self.min_freq:
                self.min_freq = target_freq
            return
        # 无历史记录时,调用父类的put逻辑
        super().put(key, value)

关键修正点

  1. 重写evict方法:必须在删除缓存元素前,先把该元素的频率存入__del_freq,否则元素被删除后无法获取历史频率。
  2. put方法逻辑顺序:先检查__del_freq,存在则优先处理(读取频率、删除记录),再执行插入;不存在才走父类逻辑。
  3. 强制删除__del_freq记录:处理完历史频率后,必须执行del self.__del_freq[key],否则该键会一直留在字典里。

测试验证

执行以下流程可以验证效果:

cache = MyLFUCache(2)
cache.put(1, 1)
cache.put(2, 2)
cache.get(1)  # 1的频率变为2
cache.put(3, 3)  # 淘汰2,__del_freq存入{2:1}
cache.put(2, 2)  # 读取__del_freq中的频率1,删除该记录
print(cache.__del_freq)  # 输出{},说明已成功删除

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 01:30:07