Python缓存lru_cache的介绍和讲解
⼀、前⾔
我们经常谈论的缓存⼀词,更多的类似于将硬盘中的数据存放到内存中以⾄于提⾼读取速度,⽐如常说的redis,就经常⽤来做数据的缓存。
Python的缓存(lru_cache)是⼀种装饰在被执⾏的函数上,将其执⾏的结果缓存起来,当下次请求的时候,如果请求该函数的传参未变则直接返回缓存起来的结果⽽不再执⾏函数的⼀种缓存装饰器。
那它和redis的区别在哪?有什么优势?怎么使⽤? 下⾯为你讲解
⽂章⽬录
⼆、举例说明
1.现在我们先不使⽤缓存来写⼀个求两数之和的函数,并调⽤执⾏它两次:
def test(a, b):
print('开始计算a+b的值...')
return a + b
print('1+2等于:', test(1,2))
print('1+2等于:', test(1,2))
执⾏结果
开始计算a+b的值...
1+2等于:3
开始计算a+b的值...
1+2等于:3
可以看到test被执⾏了两次,现在我们加上缓存再进⾏执⾏:
from functools import lru_cache
@lru_cache
def test(a, b):
print('开始计算a+b的值...')
return a + b
print(test(1,2))
print(test(1,2))
执⾏结果
开始计算a+b的值...
1+2等于:3
1+2等于:3
可以看到test函数只被执⾏了⼀次,第⼆次的调⽤直接输出了结果,使⽤了缓存起来的值。
2.当我们使⽤递归求斐波拉契数列 (斐波那契数列指的是这样⼀个数列:0,1,1,2,3,5,8,它从第3项开始,每⼀项都等于前两项之和) 的时候,缓存对性能的提升就尤其明显了:
不使⽤缓存求第40项的斐波拉契数列
import datetime
def fibonacci(num):
# 不使⽤缓存时,会重复执⾏函数
return num if num <2else fibonacci(num -1)+ fibonacci(num -2)
start = w()
print(fibonacci(40))
end = w()
print('执⾏时间', end - start)
执⾏时间
执⾏时间0:00:29.004424
使⽤缓存求第40项的斐波拉契数列:
@lru_cache
def fibonacci(num):
# 不使⽤缓存时,会重复执⾏函数
return num if num <2else fibonacci(num -1)+ fibonacci(num -2)
执⾏时间
执⾏时间0:00:00
redis支持的数据结构
两个差距是⾮常明显的,因为不使⽤缓存时,相当于要重复执⾏了很多的函数,⽽使⽤了lru_cache则把之前执⾏的函数结果已经缓存了起来,就不需要再次执⾏了。
三、lru_cache ⽤法
1.参数详解
查看lru_cache源码会发现它可以传递两个参数:maxsize、typed:
def lru_cache(maxsize=128, typed=False):
"""Least-recently-used cache decorator.
If *maxsize* is set to None, the LRU features are disabled and the cache
can grow without bound.
...
"""
1) maxsize
代表被lru_cache装饰的⽅法最⼤可缓存的结果数量 (被装饰⽅法传参不同⼀样,则结果不⼀样;如果传参⼀样则为同⼀个结果), 如果不指定传参则默认值为128,表⽰最多缓存128个返回结果,当达到了128个时,有新的结果要保存时,则会删除最旧的那个结果。如果maxsize传⼊为None则表⽰可以缓存⽆限个结果;
2)typed
默认为false,代表不区分数据类型,如果设置为True,则会区分传参类型进⾏缓存,官⽅是这样描述的:
如果typed为True,则将分别缓存不同类型的参数,
例如,f(3.0)和f(3)将被视为具有明显的结果。
但在python3.9.8版本下进⾏测试,typed为false时,按照官⽅的测试⽅法测试得到的还是会被当成不同的结果处理,这个时候typed为false还是为true都会区别缓存,这与官⽅⽂档的描述存在差异:
from functools import lru_cache
@lru_cache
def test(a):
print('函数被调⽤了...')
return a
print(test(1.0))
print(test(1))
执⾏结果
函数被调⽤了...
1.0
函数被调⽤了...
1
但如果是多参数的情况下,则会被当成⼀个结果:
from functools import lru_cache
@lru_cache
def test(a, b):
print('函数被调⽤了...')
return a , b
print(test(1.0,2.0))
print(test(1,2))
执⾏结果
函数被调⽤了...
(1.0,2.0)
(1.0,2.0)
这个时候设置typed为true时,则会区别缓存:
from functools import lru_cache
@lru_cache(typed=True)
def test(a, b):
print('函数被调⽤了...')
return a , b
print(test(1.0,2.0))
print(test(1,2))
执⾏结果
函数被调⽤了...
(1.0,2.0)
函数被调⽤了...
(1,2)
当传参个数⼤于1时,才符合官⽅的说法,不清楚是不是官⽅举例有误
2. lru_cache不⽀持可变参数
当传递的参数是dict、list等的可变参数时,lru_cache是不⽀持的,会报错:from functools import lru_cache
@lru_cache
def test(a):
print('函数被执⾏了...')
return a
print(test({'a':1}))
报错结果
TypeError: unhashable type:'dict'
四、lru_cache 与redis的区别
缓存缓存位置是否⽀持可变
参数
是否⽀持分
布式
是否⽀持过期时间
设置
⽀持的数据结构
需单独安
redis缓存在redis管理的内存中是是是⽀持5种数据结构是
lru_cache 缓存在应⽤进程的内存中,应⽤被关闭
则被清空
否否否
字典(参数为:key,结果为:
value)
五、总结
经过上⾯的分析,lru_cache 功能相对于redis来说要简单许多,但使⽤起来更加⽅便,适⽤于⼩型的单体应⽤。如果涉及的缓存的数据种类⽐较多并且想更好的管理缓存、或者需要缓存数据有过期时间(类似登录验证的token)等,使⽤redis是优于lru_cache的。
欢迎关注【曲鸟讲测试开发】,获取最新教程,⾯试经验、Python知识分享

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。