Python:小函数反复算同样输入,可以先试试 `functools.cache`

17 次浏览3 条回复

有些小脚本里会写递归,或者一个纯函数会被同样参数调用很多次。数据量不大时,标准库的 functools.cache 就能少算不少重复活。

环境:Python 3.9+。保存成 cache-demo.py

from functools import cache

@cache
def fib(n: int) -> int:
    print('calc', n)
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(6))
print(fib(6))
print(fib.cache_info())

跑一下:

python cache-demo.py

第二次 fib(6) 不会重新展开递归,结果直接从缓存里拿。

这个更适合参数可哈希、结果不会随外部状态变化的小函数。要是缓存可能无限长,或者需要过期时间,那就别硬用它了。

这个点挺容易被忽略:@cache 等于 lru_cache(maxsize=None),长跑进程里如果参数种类很多,内存会一直涨。

小脚本和递归 DP 很舒服;服务端代码我一般会更倾向显式用 lru_cache(maxsize=...),至少边界更清楚一点。

雾很大Lv1#1

这个点挺容易被忽略:@cache 等于 lru_cache(maxsize=None),长跑进程里如果参数种类很多,内存会一直涨。

小脚本和递归 DP 很舒服;服务端代码我一般会更倾向显式用 lru_cache(maxsize=...),至少边界更清楚一点。

还可以顺手提一下 fib.cache_clear(),写测试的时候挺有用。

比如同一个进程里跑多组用例,不想让上一组缓存影响下一组,就先清一下。cache_info() 也适合临时看命中率,确认是不是真的少算了。

还有个边角点:异常不会被 cache 记住。

也就是说函数这次抛错了,下次同样参数再调还是会重新执行,不会直接复用上一次的异常。调接口、读文件这类不稳定操作就更不适合拿它包一层了,还是纯计算函数最省心。