受函数式编程思想启发, 实现Python函数缓存装饰器。
#纯函数
学习函数式编程可以看JS函数式编程指南中文版
纯函数可测,可重演,最重要的是,因为同样的输入一定会产生同样的输出,使得函数的执行结果是可以被缓存的。
#实现
利用Python的闭包来实现一个缓存函数执行结果的装饰器:
1 | def cache_result(f_key=lambda x: x, expired=0): |
f_key是函数入参生成的key,相当于唯一标识一组入参,默认值是id函数(像x => x这样的)。
expired是函数缓存的有效时间。
可以看到在new_func的逻辑当中,只有当缓存里没有这个key,或者缓存的时间已经过期时,才会重新执行原来的函数func并更新缓存。
#使用
如下是一个需要读取redis数据的函数,注意这个函数不是一个纯函数,因为返回结果受到外部环境(redis缓存)影响。但这里数据的实时变化频次很低,近似地认为在一段时间(5秒)内,函数的执行结果是不变的。
1 |
|
加上缓存的装饰器后,5秒内如果调用的入参经过RedisDataHelper.get_data_key产生了已经缓存过的key,则在有效期5秒内会优先返回缓存的结果。
#延伸
Python3.2开始,functools模块内置了lru_cache装饰器,参考文档。
这个装饰器会自动把函数的入参当做缓存的key来使用,并且按照最近访问的原则控制缓存的大小。
下面是官方的一个例子,用一个无限大的缓存空间来实现fibonacci数的动态规划算法:
1 |
|
有兴趣的话可以看看源代码里lru_cache装饰器的实现,大致的思路差不多,增加了lru策略以及锁相关的实现。