Functools -- cache
Python 3.9+ 新增:
from functools import cache
它是:
✅ 无参数版 lru_cache(maxsize=None)
- 永远缓存函数的所有参数结果
适用于:
- 纯函数
- 计算昂贵且多次重复调用
- 参数可哈希(必要条件)
from functools import cache
@cache
def add(a, b):
print("running...")
return a + b
print(add(1, 2)) # 第一次执行,打印 "running..."
print(add(1, 2)) # 第二次命中缓存,不打印"running..."
📌 特点:第二次调用完全不运行函数体,速度极快。
| 功能 | cache | lru_cache |
|---|---|---|
| 需要设置 maxsize | ❌ 无 (无限缓存) | ✅ 可设置 |
| 缓存策略 | 永久缓存 | LRU(最近最少使用) |
| 清理缓存 | 手动 .cache_clear() | 手动 .cache_clear() |
| 内存使用 | 持续增长 | 有上限 |
add.cache_clear()
查看信息:
add.cache_info() # hits, misses, etc
❌ 以下不能用:
- list
- dict
- set
✅ 以下可以:
- str
- int
- tuple
- frozenset
@cache
def calc(data):
return sum(data)
calc(tuple([1, 2, 3]))
@cache
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
无限缓存可极大加速。
@cache
def get_detail(code):
return requests.get(f".../{code}").json()
⚠️ 注意:请求函数必须是纯函数,否则缓存会造成脏数据。
@cache
def load_template(name):
with open(name) as f:
return f.read()
避免重复读文件。
@cache
def stock_name(code):
return db.query(...).first().name
⚠️ 适用于数据不频繁变化的字典表、行业表、配置表。
class Stock:
@cache
def name_by_code(self, code):
return requests.get(f".../{code}")
📌 注意:
- 类方法中的第一个参数 self 也参与缓存,因此实例必须 hashable。
- 若不希望这样,应使用 @staticmethod。
@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。
你的项目中可以封装:
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):
...
| 项目 | 内容 |
|---|---|
| 导入 | from functools import cache |
| 用法 | @cache |
| 清空缓存 | func.cache_clear() |
| 查看命中率 | func.cache_info() |
| 必须可 hash | ✅ |
| 适用于 | 纯函数、重复计算、配置加载 |
| 不适用于 | async、可变对象参数 |
| 内存策略 | 永久缓存 |