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

如何让Python的functools.lru_cache返回新的可变对象实例?

如何让带lru_cache的函数返回可变对象的副本?

这个问题确实很常见——当你用functools.lru_cache缓存返回可变对象的函数时,一旦修改了返回的对象,后续调用拿到的都是被修改过的缓存版本,因为缓存的是对象本身的引用,而不是独立的副本。

先看看你遇到的场景:

import functools
@functools.lru_cache()
def f():
    x = [0, 1, 2] # 代表某个耗时计算
    return x

a = f()
a.append(3)
b = f()
print(a) # [0, 1, 2, 3]
print(b) # [0, 1, 2, 3]

这里因为缓存的是列表对象的引用,修改a就等于直接修改了缓存池里的对象,所以后续调用f()拿到的b自然也是被修改后的版本。

你提到让调用者手动用.copy()的方法虽然可行,但不够优雅,最好能在函数内部解决这个问题。我们可以自己封装一个增强版的lru_cache装饰器,支持copy参数,默认和原版行为一致,开启后自动返回缓存结果的副本。

实现方案

import functools
from copy import deepcopy

def lru_cache(maxsize=128, typed=False, copy=False):
    if not copy:
        # 不开启复制时,直接返回原版lru_cache,保证兼容性
        return functools.lru_cache(maxsize, typed)
    def decorator(f):
        # 先创建原版的缓存函数
        cached_func = functools.lru_cache(maxsize, typed)(f)
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            # 每次返回缓存结果的深拷贝
            return deepcopy(cached_func(*args, **kwargs))
        return wrapper
    return decorator

测试效果

# 默认不开启copy,和原版lru_cache行为完全一致
@lru_cache()
def f():
    x = [0, 1, 2]
    return x

a = f()
a.append(3)
b = f()
print(a) # [0, 1, 2, 3]
print(b) # [0, 1, 2, 3]

# 开启copy=True,每次返回独立的副本
@lru_cache(copy=True)
def f():
    x = [0, 1, 2]
    return x

a = f()
a.append(3)
b = f()
print(a) # [0, 1, 2, 3]
print(b) # [0, 1, 2]

关键点说明

  • 兼容性优先:当copy=False时,直接复用原版functools.lru_cache,不会改变原有代码的行为;
  • 深拷贝保证独立性:用deepcopy而不是浅拷贝,能处理嵌套的可变对象(比如包含字典的列表);如果你的场景只需要浅拷贝,可以换成copy.copy来提升性能;
  • 保留原函数元信息:通过functools.wraps(f)确保装饰后的函数保留原函数的名称、文档字符串等元信息,方便调试和文档生成。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 03:55:49