Python 函数缓存实战:用 functools.lru_cache 给重复查询加速

举报
yd_70403235 发表于 2026/11/05 15:46:07 2026/11/05
【摘要】 同一个纯函数被反复调用、每次都重新算一遍,是很多小程序里悄悄浪费时间的来源。本文用 Python 标准库 functools.lru_cache 给耗时查询加一层缓存:第一次计算后结果直接复用,重复调用几乎零开销,并用 cache_info 观察命中情况;同时提醒哪些函数不适合缓存,避免踩坑。

背景

写工具脚本时经常遇到这种情况:一个函数输入固定、输出固定,却因为被循环反复调用而每次都重新算。比如根据用户 ID 查配置、按日期算统计口径、递归算斐波那契数列,输入相同结果必然相同,重复计算纯属浪费。自己手写一个 dict 做缓存当然可以,但要处理缓存上限、线程安全和统计信息就麻烦了。Python 标准库已经给了现成的工具:functools.lru_cache,一个装饰器就能给函数加上带淘汰策略的缓存。

实现思路

分三步走。第一步,找到“纯函数”:同样的输入永远返回同样的输出,中间不读写文件、不改全局状态,这样的函数才适合缓存。第二步,在函数上加 @lru_cache(maxsize=128),maxsize 控制缓存最多保留多少条记录,超出后按最久未使用淘汰;不传参数时默认 maxsize=128。第三步,用函数自带的 cache_info() 查看命中(hits)与未命中(misses),确认缓存真的在起作用;需要清空时调用 cache_clear()。

需要注意两点:一是被缓存函数的参数必须是可哈希的,列表、字典这类不可哈希类型传进去会直接报错;二是函数返回值如果会被调用方原地修改,缓存里存的也是同一份对象,改动会污染后续命中结果,这类情况不要缓存,或者返回副本。

代码

import time
from functools import lru_cache


@lru_cache(maxsize=128)
def slow_query(user_id: int) -> str:
    """模拟一次耗时查询:同样输入的结果固定,适合缓存。"""
    time.sleep(0.2)
    return f"user-{user_id}-profile"


def main() -> None:
    start = time.perf_counter()
    print(slow_query(7))   # 第 1 次:未命中,真实计算
    print(slow_query(7))   # 第 2 次:命中缓存,直接返回
    print(slow_query(8))   # 第 3 次:新输入,未命中
    print(slow_query(7))   # 第 4 次:再次命中
    elapsed = time.perf_counter() - start
    print(f"4 次调用总耗时: {elapsed:.2f} 秒")
    print(slow_query.cache_info())


if __name__ == "__main__":
    main()

验证结果

已在本地 Python 3.12 实测。4 次调用总耗时约 0.40 秒:只有 user_id 为 7 和 8 的两次真正执行了 0.2 秒的“查询”,其余两次命中缓存几乎不耗时;若没有缓存,4 次调用需要约 0.80 秒。cache_info() 输出为 CacheInfo(hits=2, misses=2, maxsize=128, currsize=2),命中 2 次、未命中 2 次、当前缓存 2 条,与预期完全一致。

注意事项

  • 只缓存纯函数:依赖当前时间、随机数、文件内容或数据库状态的函数,结果会随时间变化,缓存会返回过期数据。
  • 参数必须可哈希:传列表或字典会报 TypeError,必要时先转成元组再调用。
  • 返回可变对象要小心:调用方如果原地修改返回值,会污染缓存;可返回副本,或缓存不可变结果。
  • 缓存不是越大越好:maxsize 设得太大,长期运行的程序会越占越多内存;128 是多数场景够用的起点,命中率低时再调大。
  • 需要手动失效时调用 cache_clear(),比如底层数据更新后,让下一次调用重新计算。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。