如何让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
相关产品推荐
相关产品推荐

