
Python函数记忆化(Memoization)本质是用空间换时间,把已计算的结果缓存起来,避免重复执行耗时逻辑。最常用、最简洁的实现方式是使用内置的 @functools.lru_cache,但理解其底层思路有助于自定义缓存行为或适配特殊场景。
核心原理:用字典做键值映射
记忆化的关键在于——把函数的参数(必须可哈希)作为 key,把返回值作为 value,存进一个字典。下次调用相同参数时,直接查字典返回,跳过原函数体执行。
- 参数需可哈希(如
int、str、tuple),不可哈希类型(如list、dict)需先标准化(例如转为tuple或 JSON 字符串) - 默认不处理关键字参数顺序差异(
f(a=1, b=2)和f(b=2, a=1)在普通 dict 实现中可能被当成不同 key,需统一为规范化的 frozenset 或 tuple) - 缓存字典应绑定在装饰器内部或函数对象上,避免全局污染和多函数冲突
手动实现一个基础版 @memo 装饰器
不依赖 lru_cache,用闭包+字典即可写出清晰易懂的记忆化装饰器:
def memo(func):
cache = {}
def wrapper(*args, **kwargs):
# 将 args 和 frozenset(kwargs.items()) 组合成可哈希的 key
key = (args, frozenset(kwargs.items()))
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return wrapper
<p>@memo
def fib(n):
return n if n < 2 else fib(n-1) + fib(n-2)
注意:该版本无大小限制、无超时、不支持清除,适合学习和轻量场景。
立即学习“Python免费学习笔记(深入)”;
进阶要点:控制缓存生命周期与策略
实际项目中常需更精细的控制,比如限制缓存数量、支持手动清除、区分不同实例方法等:
- 用
functools.lru_cache(maxsize=128)自动淘汰最近最少使用的项,maxsize=None表示无上限 - 调用
func.cache_clear()可清空缓存(lru_cache提供该方法) - 对类方法做记忆化,需注意是否要按实例隔离缓存(默认共享),可用
functools.cached_property或自定义 descriptor 实现单实例缓存 - 若需线程安全,原始字典需加锁,或改用
threading.RLock包裹访问逻辑
慎用场景与常见陷阱
记忆化不是万能药,滥用反而引发问题:
- 函数有副作用(如修改全局变量、写文件、发请求)——缓存后副作用只发生一次,后续调用“静默跳过”
- 参数含不可哈希对象(如
list)且未预处理,会直接报TypeError - 内存敏感场景下缓存无限增长(尤其参数组合多、返回值大),应设
maxsize并监控cache_info() - 动态数据(如实时价格、数据库结果)不应缓存,或需配合 TTL(生存时间)机制
掌握从字典映射到装饰器封装的推演过程,比死记 lru_cache 参数更有价值。真正用好记忆化,关键是判断「输入是否稳定」「输出是否确定」「缓存是否值得」这三点。










