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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:40:39