python自带缓存lru_cache用法及扩展(详细)
本篇博客将结合python官方文档和源码详细讲述lru_cache缓存方法是怎么实现, 它与redis缓存的区别是什么, 在使用时碰上functiontools.wrap装饰器时会发生怎样的变化,以及了解它给我们提供了哪些功能然后在其基础上实现我们自制的缓存方法my_cache。
1. lru_cache的使用
1.1 参数详解
以下是lru_cache方法的实现,我们看出可供我们传入的参数有2个maxsize和typed,如果不传则maxsize的默认值为128,typed的默认值为False。其中maxsize参数表示是的被装饰的方法最大可缓存结果数量, 如果是默认值128则表示被装饰方法最多可缓存128个返回结果,如果maxsize传入为None则表示可以缓存无限个结果,你可能会疑惑被装饰方法的n个结果是怎么来的,打个比方被装饰的方法为def add(a, b):当函数被lru_cache装饰时,我们调用add(1, 2)和add(3, 4)将会缓存不同的结果。如果 typed 设置为true,不同类型的函数参数将被分别缓存。例如, f(3) 和 f(3.0) 将被视为不同而分别缓存。
def lru_cache(maxsize=128, typed=False):
if isinstance(maxsize, int):
if maxsize < 0:
maxsize = 0
elif maxsize is not None:
raise TypeError('Expected maxsize to be an integer or None')
def decorating_function(user_function):
wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
return update_wrapper(wrapper, user_function)
return decorating_function
1.2 基本用法
在我们编写接口时可能需要缓存一些变动不大的数据如配置信息,我们可能编写如下接口:
@api.route("/user/info", methods=["GET"])
@functools.lru_cache()
@login_require
def get_userinfo_list():
userinfos = UserInfo.query.all()
userinfo_list = [user.to_dict() for user in userinfos]
return jsonify(userinfo_list)
我们缓存了从数据库查询的用户信息,下次再调用这个接口时将直接返回用户信息列表而不需要重新执行一遍数据库查询逻辑,可以有效较少IO次数,加快接口反应速度。
1.3 进阶用法
还是以上面的例子,如果发生用户的删除或者新增时,我们再请求用户接口时仍然返回的是缓存中的数据,这样返回的信息就和我们数据库中的数据就会存在差异,所以当发生用户新增或者删除时,我们需要清除原先的缓存,然后再请求用户接口时可以重新加载缓存。
@api.route("/user/info", methods=["POST"])
@functools.lru_cache()
@login_require
def add_user():
user = UserInfo(name="李四")
db.session.add(user)
db.session.commit()
# 清除get_userinfo_list中的缓存
get_userinfo_list = current_app.view_functions["api.get_machine_list"]
cache_info = get_userinfo_list.cache_info()
# cache_info 具名元组,包含命中次数 hits,未命中次数 misses ,最大缓存数量 maxsize 和 当前缓存大小 currsize
# 如果缓存数量大于0则清除缓存
if cache_info[3] > 0:
get_userinfo_list.cache_clear()
return jsonify("新增用户成功")
在上面这个用法中我们,如果我们把lru_cache装饰器和login_require装饰器调换位置时,上述的写法将会报错,这是因为login_require装饰器中用了functiontools.wrap模块进行装饰导致的,具原因我们在下节解释, 如果想不报错得修改成如下写法。
@api.route("/user/info", methods=["POST"])
@login_require
@functools.lru_cache()
def add_user():
user = UserInfo(name="李四")
db.session.add(user)
db.session.commit()
# 清除get_userinfo_list中的缓存
get_userinfo_list = current_app.view_functions["api.get_machine_list"]
cache_info = get_userinfo_list.__wrapped__.cache_info()
# cache_info 具名元组,包含命中次数 hits,未命中次数 misses ,最大缓存数量 maxsize 和 当前缓存大小 currsize
# 如果缓存数量大于0则清除缓存
if cache_info[3] > 0:
get_userinfo_list.__wrapped__.cache_clear()
return jsonify("新增用户成功")
2. functiontools.wrap装饰器对lru_cache的影响
在上节我们看到,因为@login_require和@functools.lru_cache()装饰器的顺序不同, 就导致了程序是否报错, 其中主要涉及到两点:
- login_require装饰器中是否用了@functiontools.wrap()装饰器
- @login_require和@functools.lru_cache()装饰器的执行顺序问题
当我们了解完这两点后就可以理解上述写法了。
2.1 多个装饰器装饰同一函数时的执行顺序
这里从其他地方盗了一段代码来解释一下,如下:
def decorator_a(func):
print('Get in decorator_a')
def inner_a(*args,**kwargs):
print('Get in inner_a')
res = func(*args,**kwargs)
return res
return inner_a
def decorator_b(func):
print('Get in decorator_b')
def inner_b(*args,**kwargs):
print('Get in inner_b')
res = func(*args,**kwargs)
return res
return inner_b
@decorator_b
@decorator_a
def f(x):
print('Get in f')
return x * 2
f(1)
输出结果如下:
'Get in decorator_a'
'Get in decorator_b'
'Get in inner_b'
'Get in inner_a'
'Get in f'
是不是很像django中的中间件的执行顺序,其实原理都差不多。
2.2 functiontools.wrap原理
引用其他博主的描述:
Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。写一个decorator的时候,最好在实现之前加上functools的wrap,它能保留原有函数的名称和docstring。
补充:为了访问原函数此函数会设置一个
__wrapped__属性指向原函数, 这样就可以解释上面1.3节中我们的写法了。
2.3 使用wrap装饰器前后的变化
未完待续。。。。。。。。。
3. 自制简易的my_cache
3.1 lru_cache提供的功能
lru_cache缓存装饰器提供的功能有:
- 缓存被装饰对象的结果(基础功能)
- 获取缓存信息
- 清除缓存内容
- 根据参数变化缓存不同的结果
- LRU算法当缓存数量大于设置的maxsize时清除最不常使用的缓存结果
从列出的功能可知,python自带的lru_cache缓存方法可以满足我们日常工作中大部分需求, 可是它不包含一个重要的特性就是,超时自动删除缓存结果,所以在我们自制的my_cache中我们将实现缓存的超时过期功能。
3.2 cache的核心部件
在作用域内存在一个相对全局的字典变量cache={}
在作用域内设置相对全局的变量包含命中次数 hits,未命中次数 misses ,最大缓存数量 maxsize和 当前缓存大小 currsize
第二点中的缓存信息中增加缓存加入时间和缓存有效时间
3.3 my_cache的实现
待实现。。。。。。。。。。。。
4. lru_cache缓存和redis缓存的区别
| 比较类型 | lru_cache | redis |
|---|---|---|
| 缓存类型 | 缓存在app进程内存中 | 缓存在redis管理的内存中 |
| 分布式 | 只缓存在单个app进程中 | 可做分布式缓存 |
| 数据类型 | hash 参数作为key,返回结果为value | 有5种类型的数据结构 |
| 适用场景 | 比较小型的系统、单体应用 | 常用的缓存解决方案 |
| 功能 | 缓存功能但是缺少过期时间控制,但是使用上更加便捷 | 具备缓存需要的各种要素 |
5. 总结
综上所述,python自带的缓存功能使用于稍微小型的单体应用。优点是可以很方便的根据传入不同的参数缓存对应的结果, 并且可以有效控制缓存的结果数量,在超过设置数量时根据LRU算法淘汰命中次数最少的缓存结果。缺点是没有办法对缓存过期时间进行设置。
python自带缓存lru_cache用法及扩展(详细)的更多相关文章
- Python @函数装饰器及用法(超级详细)
函数装饰器的工作原理是怎样的呢?假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示: #funA 作为装饰器函数 def funA(fn): #... fn() # 执行传入的fn参 ...
- Python正则式的基本用法
Python正则式的基本用法 1.1基本规则 1.2重复 1.2.1最小匹配与精确匹配 1.3前向界定与后向界定 1.4组的基本知识 2.re模块的基本函数 2.1使用compile加速 2.2 ma ...
- python笔记之常用模块用法分析
python笔记之常用模块用法分析 内置模块(不用import就可以直接使用) 常用内置函数 help(obj) 在线帮助, obj可是任何类型 callable(obj) 查看一个obj是不是可以像 ...
- python中ConfigParse模块的用法
ConfigParser 是Python自带的模块, 用来读写配置文件, 用法及其简单. 配置文件的格式是: [...]包含的叫section section 下有option=value这样的键值 ...
- django之缓存的用法, 文件形式与 redis的基本使用
django的缓存的用法讲解 1. django缓存: 缓存的机制出现主要是缓解了数据库的压力而存在的 2. 动态网站中,用户的请求都会去数据库中进行相应的操作,缓存的出现是提高了网站的并发量 3. ...
- Python numpy中矩阵的用法总结
关于Python Numpy库基础知识请参考博文:https://www.cnblogs.com/wj-1314/p/9722794.html Python矩阵的基本用法 mat()函数将目标数据的类 ...
- python集合与字典的用法
python集合与字典的用法 集合: 1.增加 add 2.删除 •del 删除集合 •discard(常用)删除集合中的元素 #删除一个不存在的元素不会报错 •remove 删除一个不存在的 ...
- Python中int()函数的用法浅析
int()是Python的一个内部函数 Python系统帮助里面是这么说的 >>> help(int) Help on class int in module __builti ...
- python骚操作---Print函数用法
---恢复内容开始--- python骚操作---Print函数用法 在 Python 中,print 可以打印所有变量数据,包括自定义类型. 在 3.x 中是个内置函数,并且拥有更丰富的功能. 参数 ...
随机推荐
- JUC锁种类总结
在并发编程中有各种各样的锁,有的锁对象一个就身兼多种锁身份,所以初学者常常对这些锁造成混淆,所以这里来总结一下这些锁的特点和实现. 乐观锁.悲观锁 悲观锁 悲观锁是最常见的锁,我们常说的加锁指的也就是 ...
- JS逆向课程笔记
扩展知识 Sources-js代码格式化
- 三步删除U深度,老毛桃,大白菜捆绑软件!!
①需要下载一个软件Fbinst Tool(万能U盘制作工具).链接:http://pan.baidu.com/s/1mim9Zxm 密码:7poo②插入U盘,直接打开Fbinst Tool.首先是U深 ...
- unctf2020 部分简单题题解
unctf2020 水一波简单题..菜狗前来报道..大佬轻喷..如果有时间做题就好了呜呜呜 misc 1.baba_is_you 题目告诉我们,了解png文件格式. 下载得到一张png格式的图片. 用 ...
- Druid配置和初始化参数 转发地址图片有
配置数据源 1.添加上 Druid 数据源依赖. <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dep ...
- 如何修改IDM下载器的临时文件夹位置
所有的应用程序在下载时,都会有一些默认的选项.比如产生的临时文件存放在C盘目录下,或者定期自动更新等设置.那么当我们的计算机上安装了很多程序之后,C盘的空间就会渐渐地变小了,从而有了空间不足等等情况, ...
- ABBYY FineReader 15新增智能PDF文档转换功能
ABBYY FineReader 15(Windows系统)新增智能PDF文档转换功能,可自动检测导入PDF数字文档的文本层质量,确保转变为可编辑格式后的准确结果:从表单字段和文本框中提取文本,准确保 ...
- 思维导图软件iMindMap幻灯片设置功能介绍
我们运用iMindMap演示来播放幻灯片时,有没想过,我怎么改动幻灯片的播放时长,怎么设置它的播放速度这些基本设置呢.下面,本文就告诉你,我们该去哪里修改这些iMindMap幻灯片设置: 我们打开iM ...
- 如何使用ABBYY FineReader 用户词典识别专业术语?
ABBYY FineReader 15可以说是比较新的版本,在这个版本中能运用强大的光学字符识别技术对PDF文档扫描件.图像等文件进行OCR识别.在识别的过程中,会使用其内置的词典检查识别文字,以获得 ...
- MathType可以和哪些Microsoft Office版本一起使用?
Office类软件可能是我们碰到电脑后,最先接触到的电脑软件了.尤记得,当初的微机课一开始就会讲word和excel的使用,一开始可能学不太明白,但后来越来越频繁的使用office软件,不说offic ...