示例说明
lru_cache 是标准库里最廉价的提速手段:装饰一行,纯函数的重复调用直接命中缓存。maxsize 的选择决定效果——工作集大于容量时 LRU 频繁淘汰,命中率塌方;cache_info 返回的命中与未命中计数是调优的唯一直接证据,容量翻倍后命中率是否上升由数据说话。
技术要点
- maxsize 应覆盖实际工作集规模
- cache_info 的命中率是调优依据
- 列表参数转元组才可哈希缓存
代码解读
price_of 装饰后查表免于重复执行;bundle_price 的参数声明为元组——列表不可哈希会直接 TypeError,这是最常见的接入坑。主循环对六十个键查三轮:首轮全未命中,后两轮全命中。cache_report 汇总命中率与当前占用,cache_clear 演示失效手段。
使用建议
有副作用的函数绝不能上 lru_cache,缓存会吞掉副作用。按调用方分布做容量规划,长尾键可拆独立缓存。跨进程缓存换 Redis,进程内缓存就用它。
代码示例
import functools
import time
def load_catalog():
return {f'SKU-{i}': {'price': i * 3, 'stock': i % 7} for i in range(500)}
CATALOG = load_catalog()
@functools.lru_cache(maxsize=256)
def price_of(sku: str) -> int:
# 纯查表函数:缓存命中即免重复计算
return CATALOG[sku]['price']
@functools.lru_cache(maxsize=64)
def bundle_price(skus: tuple[str, ...]) -> int:
# 参数必须是可哈希的:列表要转元组
return sum(price_of(s) for s in skus)
def cache_report():
info = price_of.cache_info()
total = info.hits + info.misses
hit_rate = info.hits / total if total else 0.0
return {
'hit_rate': round(hit_rate, 3),
'hits': info.hits,
'misses': info.misses,
'maxsize': info.maxsize,
'currsize': info.currsize,
}
skus = [f'SKU-{i}' for i in range(60)]
start = time.perf_counter()
for _ in range(3):
for sku in skus:
price_of(sku)
elapsed = time.perf_counter() - start
print(cache_report())
print(f'60 keys x 3 rounds in {elapsed * 1000:.1f}ms')
# 命中率下降说明工作集超过 maxsize:调大或按业务键分布重新评估
price_of.cache_clear()