Skip to main content
Documents
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Functools -- cache

⭐ 1. functools.cache 是什么?

Python 3.9+ 新增:

from functools import cache

它是:

无参数版 lru_cache(maxsize=None)

  • 永远缓存函数的所有参数结果

适用于:

  • 纯函数
  • 计算昂贵且多次重复调用
  • 参数可哈希(必要条件)

⭐ 2. 基本用法

from functools import cache

@cache
def add(a, b):
    print("running...")
    return a + b

print(add(1, 2))   # 第一次执行,打印 "running..."
print(add(1, 2))   # 第二次命中缓存,不打印"running..."

📌 特点:第二次调用完全不运行函数体,速度极快。

⭐ 3. 与 lru_cache 的区别

功能 cache lru_cache
需要设置 maxsize ❌ 无 (无限缓存) ✅ 可设置
缓存策略 永久缓存 LRU(最近最少使用)
清理缓存 手动 .cache_clear() 手动 .cache_clear()
内存使用 持续增长 有上限

⭐ 4. 缓存的清理

add.cache_clear()

查看信息:

add.cache_info()   # hits, misses, etc

⭐ 5. 参数必须可哈希(重要)

❌ 以下不能用:

  • list
  • dict
  • set

✅ 以下可以:

  • str
  • int
  • tuple
  • frozenset

⭐ 6. 使用 tuple 包装解决不可 hash 的参数

@cache
def calc(data):
    return sum(data)

calc(tuple([1, 2, 3]))

⭐ 7. 使用场景(非常重要)

1)重复性高的计算任务

@cache
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

无限缓存可极大加速。

2)数据爬虫重复请求(例如股票数据)

@cache
def get_detail(code):
    return requests.get(f".../{code}").json()

⚠️ 注意:请求函数必须是纯函数,否则缓存会造成脏数据。

3)配置加载 / 模板读取(文件 I/O)

@cache
def load_template(name):
    with open(name) as f:
        return f.read()

避免重复读文件。

4)数据库查询(只读场景)

@cache
def stock_name(code):
    return db.query(...).first().name

⚠️ 适用于数据不频繁变化的字典表、行业表、配置表。

⭐ 8. 在类中使用 @cache

class Stock:
    @cache
    def name_by_code(self, code):
        return requests.get(f".../{code}")

📌 注意:

  • 类方法中的第一个参数 self 也参与缓存,因此实例必须 hashable。
  • 若不希望这样,应使用 @staticmethod。

⭐ 9. 与 async 不兼容(重要)

@cache 不能用在 async 函数上。

异步缓存需要:

  • async_lru
  • 或你自己维护 dict 缓存

例如:

from functools import lru_cache
import asyncio

@lru_cache
def fetch(url):
    return asyncio.run(client.get(url))   # ❌ 不推荐

📌 async 建议我帮你写单独的 async cache。

⭐ 10. 真实工程最佳实践:封装缓存装饰器

你的项目中可以封装:

from functools import cache
from loguru import logger

def cached(func):
    cached_func = cache(func)

    def wrapper(*args, **kwargs):
        logger.debug(f"Cache => {func.__name__}({args}, {kwargs})")
        return cached_func(*args, **kwargs)

    wrapper.cache_clear = cached_func.cache_clear
    return wrapper

使用:

@cached
def get_stock(code):
    ...

⭐ 11. 终极速查表

项目 内容
导入 from functools import cache
用法 @cache
清空缓存 func.cache_clear()
查看命中率 func.cache_info()
必须可 hash
适用于 纯函数、重复计算、配置加载
不适用于 async、可变对象参数
内存策略 永久缓存