Python缓存lru_cache的介绍和讲解

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 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 < 2 else fibonacci(num - 1) + fibonacci(num - 2)
start = datetime.datetime.now()
print(fibonacci(40))
end = datetime.datetime.now()
print('执行时间', end - start)

执行时间


执行时间 0:00:29.004424


使用缓存求第40项的斐波拉契数列:

@lru_cache
def fibonacci(num):
  # 不使用缓存时,会重复执行函数
    return num if num < 2 else fibonacci(num - 1) + fibonacci(num - 2)

两个差距是非常明显的,因为不使用缓存时,相当于要重复执行了很多的函数,而使用了lru_cache则把之前执行的函数结果已经缓存了起来,就不需要再次执行了。


三、lru_cache 用法


1.参数详解

查看lru_cache源码会发现它可以传递两个参数:maxsizetyped

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的。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
5月前
|
缓存 数据库 索引
如何优化Python Web应用的性能,包括静态资源加载、缓存策略等?
```markdown 提升Python Web应用性能的关键点:压缩合并静态资源,使用CDN,设置缓存头;应用和HTTP缓存,ETag配合If-None-Match;优化数据库索引和查询,利用数据库缓存;性能分析优化代码,避免冗余计算,使用异步处理;选择合适Web服务器并调整参数;部署负载均衡器进行横向扩展。每一步都影响整体性能,需按需调整。 ```
46 4
|
5月前
|
缓存 NoSQL Redis
Python缓存技术(Memcached、Redis)面试题解析
【4月更文挑战第18天】本文探讨了Python面试中关于Memcached和Redis的常见问题,包括两者的基础概念、特性对比、客户端使用、缓存策略及应用场景。同时,文章指出了易错点,如数据不一致和缓存淘汰策略,并提供了实战代码示例,帮助读者掌握这两款内存键值存储系统的使用和优化技巧。通过理解其核心特性和避免常见错误,可以提升在面试中的表现。
76 2
|
8天前
|
缓存 Java Python
python垃圾回收&缓存机制
python垃圾回收&缓存机制
|
2月前
|
缓存 Python
Python中更好用的函数运算缓存
Python中更好用的函数运算缓存
|
3月前
|
缓存 Python
在Python中,`functools`模块提供了一个非常有用的装饰器`lru_cache()`,它实现了最近最少使用(Least Recently Used, LRU)缓存策略。
在Python中,`functools`模块提供了一个非常有用的装饰器`lru_cache()`,它实现了最近最少使用(Least Recently Used, LRU)缓存策略。
|
2月前
|
缓存 NoSQL 网络安全
【Azure Redis 缓存】 Python连接Azure Redis, 使用redis.ConnectionPool 出现 "ConnectionResetError: [Errno 104] Connection reset by peer"
【Azure Redis 缓存】 Python连接Azure Redis, 使用redis.ConnectionPool 出现 "ConnectionResetError: [Errno 104] Connection reset by peer"
|
2月前
|
缓存 监控 NoSQL
【Azure Redis 缓存】使用Python代码获取Azure Redis的监控指标值 (含Powershell脚本方式)
【Azure Redis 缓存】使用Python代码获取Azure Redis的监控指标值 (含Powershell脚本方式)
|
2月前
|
存储 缓存 算法
Python 从零开始实现一个简单的LRU缓存
Python 从零开始实现一个简单的LRU缓存
30 0
|
5月前
|
缓存 数据处理 Python
python读取文件到缓存
python读取文件到缓存
62 1
|
5月前
|
缓存 NoSQL Redis
在Python Web开发过程中:数据库与缓存,除了Redis是内存数据库以外,还有哪些原因使其运行速度快?
Redis在Python Web开发中快速的原因:内存存储、多样化数据结构(如字符串、哈希、列表等)简化数据模型,单线程处理提高效率,结合非阻塞I/O;RDB和AOF提供持久化保障;TCP+二进制协议减少网络开销;管道技术提升通信效率。这些设计使Redis能高效处理高并发请求。
54 3
下一篇
无影云桌面