Python闭包递归问题:带缓存限制的memoize装饰器功能实现咨询
问题分析
你的原装饰器有几个关键问题导致不符合预期:
- 当缓存条目达到上限后,调用新参数时仅打印提示消息,没有执行原函数也无返回值,会导致调用
fat()返回None,这显然不对。 count变量统计的是缓存被添加的次数,而非当前缓存的实际条目数。比如重复调用同一个参数,count不会增加,但缓存里始终只有这一个条目——这意味着如果你的limit=5,但你只调用了3个不同的参数,即使重复调用这3个,count也不会到5,但如果之后调用第6个新参数,就会触发限制,这和“缓存结果存储上限”的需求不符。
正确实现方式
根据需求,我们需要让装饰器严格限制缓存中存储的条目数量,当达到上限时,可以选择两种实用逻辑:
1. 缓存满后不再缓存新结果,但正常执行原函数
这种逻辑最简单:当缓存条目数小于limit时,正常缓存结果;当缓存满了,新的参数直接执行原函数,不缓存结果。
# -*- coding: utf-8 -*- def memoize(limit, *, message='Limit exceeded'): def inner(func): cache = {} def wrapped(number): if number in cache: # 命中缓存,直接返回结果 return cache[number] # 未命中缓存,先执行原函数获取结果 result = func(number) if len(cache) < limit: # 缓存未满,将新结果存入缓存 cache[number] = result else: # 缓存已满,打印提示(可选逻辑) print(message) return result return wrapped return inner @memoize(5) def fat(x): if x < 2: return 1 return x * fat(x - 1)
改动说明:
- 移除了错误的
count变量,改用len(cache)直接判断缓存条目数,这才是真正的“存储上限”。 - 无论缓存是否满,未命中时都会先执行原函数获取结果,保证函数始终有有效返回值。
- 只有当缓存未满且未命中缓存时,才将结果存入缓存。
2. 实现LRU缓存淘汰策略(更实用的生产场景)
如果希望缓存始终保持不超过limit的条目数,同时自动淘汰最近最少使用的条目来存储新结果,这是工业界常用的缓存策略。我们可以用OrderedDict来手动实现:
# -*- coding: utf-8 -*- from collections import OrderedDict def memoize(limit, *, message='Limit exceeded'): def inner(func): # OrderedDict可记录访问顺序,用来实现LRU逻辑 cache = OrderedDict() def wrapped(number): if number in cache: # 命中缓存,将该条目移到末尾(标记为最近使用) cache.move_to_end(number) return cache[number] # 未命中缓存,执行原函数获取结果 result = func(number) if len(cache) >= limit: # 缓存已满,删除最久未使用的条目(OrderedDict的第一个元素) cache.popitem(last=False) print(message) # 将新结果存入缓存末尾 cache[number] = result return result return wrapped return inner @memoize(5) def fat(x): if x < 2: return 1 return x * fat(x - 1)
改动说明:
- 使用
OrderedDict维护缓存的访问顺序:命中缓存时将条目移到末尾,缓存满时删除最前面的条目(最久未使用)。 - 始终保证缓存条目数不超过
limit,同时保留最常用的缓存结果。 - 同样保证函数始终返回正确的计算结果,不会出现
None的异常情况。
内容的提问来源于stack exchange,提问作者ThiaguinhoO
相关产品推荐
相关产品推荐

