Python缓存lru_cache的介绍和讲解

本文涉及的产品
云数据库 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
目录
相关文章
|
2天前
|
缓存 数据库 索引
如何优化Python Web应用的性能,包括静态资源加载、缓存策略等?
```markdown 提升Python Web应用性能的关键点:压缩合并静态资源,使用CDN,设置缓存头;应用和HTTP缓存,ETag配合If-None-Match;优化数据库索引和查询,利用数据库缓存;性能分析优化代码,避免冗余计算,使用异步处理;选择合适Web服务器并调整参数;部署负载均衡器进行横向扩展。每一步都影响整体性能,需按需调整。 ```
23 4
|
2天前
|
缓存 Python
给我一些具体的例子,说明如何在Python中使用缓存和释放来避免内存溢出。
给我一些具体的例子,说明如何在Python中使用缓存和释放来避免内存溢出。
15 0
|
2天前
|
存储 缓存 NoSQL
在Python Web开发过程中:数据库与缓存,Redis在Web开发中的常见应用场景有哪些?
Redis在Python Web开发中常用于缓存、会话管理、分布式锁、排行榜、消息队列和实时分析。作为内存数据存储,它提供高效的数据结构(如字符串、哈希、列表、集合、有序集合),支持会话存储、互斥操作、计数与排名、队列实现及实时数据处理。其高速性能和丰富功能使其成为多场景下的理想选择。
26 5
|
2天前
|
缓存 NoSQL Redis
Python缓存技术(Memcached、Redis)面试题解析
【4月更文挑战第18天】本文探讨了Python面试中关于Memcached和Redis的常见问题,包括两者的基础概念、特性对比、客户端使用、缓存策略及应用场景。同时,文章指出了易错点,如数据不一致和缓存淘汰策略,并提供了实战代码示例,帮助读者掌握这两款内存键值存储系统的使用和优化技巧。通过理解其核心特性和避免常见错误,可以提升在面试中的表现。
31 2
|
2天前
|
缓存 数据处理 Python
python读取文件到缓存
python读取文件到缓存
15 1
|
2天前
|
缓存 NoSQL 关系型数据库
在Python Web开发过程中:数据库与缓存,MySQL和NoSQL数据库的主要差异是什么?
MySQL与NoSQL的主要区别在于数据结构、查询语言和可扩展性。MySQL是关系型数据库,依赖预定义的数据表结构,使用SQL进行复杂查询,适合垂直扩展。而NoSQL提供灵活的存储方式(如JSON、哈希表),无统一查询语言,支持横向扩展,适用于处理大规模、非结构化数据和高并发场景。选择哪种取决于应用需求、数据模型及扩展策略。
170 0
|
2天前
|
SQL 缓存 数据库
在Python Web开发过程中:数据库与缓存,如何使用ORM(例如Django ORM)执行查询并优化查询性能?
在Python Web开发中,使用ORM如Django ORM能简化数据库操作。为了优化查询性能,可以:选择合适索引,避免N+1查询(利用`select_related`和`prefetch_related`),批量读取数据(`iterator()`),使用缓存,分页查询,适时使用原生SQL,优化数据库配置,定期优化数据库并监控性能。这些策略能提升响应速度和用户体验。
18 0
|
2天前
|
缓存 Python
python本地缓存cacheout
python本地缓存cacheout
|
2天前
|
缓存 NoSQL Redis
在Python Web开发过程中:数据库与缓存,除了Redis是内存数据库以外,还有哪些原因使其运行速度快?
Redis在Python Web开发中快速的原因:内存存储、多样化数据结构(如字符串、哈希、列表等)简化数据模型,单线程处理提高效率,结合非阻塞I/O;RDB和AOF提供持久化保障;TCP+二进制协议减少网络开销;管道技术提升通信效率。这些设计使Redis能高效处理高并发请求。
25 3
|
2天前
|
缓存 NoSQL Redis
如何在Python中使用Redis或Memcached进行缓存?
如何在Python中使用Redis或Memcached进行缓存?
30 2