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)
关键修正点
- 重写evict方法:必须在删除缓存元素前,先把该元素的频率存入
__del_freq,否则元素被删除后无法获取历史频率。 - put方法逻辑顺序:先检查
__del_freq,存在则优先处理(读取频率、删除记录),再执行插入;不存在才走父类逻辑。 - 强制删除__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
相关产品推荐
相关产品推荐

