python自带缓存lru_cache用法及扩展的使用
作者:种树飞 发布时间:2022-06-16 08:48:15
目录
1. lru_cache的使用
1.1 参数详解
1.2 基本用法
1.3 进阶用法
2. functiontools.wrap装饰器对lru_cache的影响
2.1 多个装饰器装饰同一函数时的执行顺序
2.2 functiontools.wrap原理
2.3 使用wrap装饰器前后的变化
3. 自制简易的my_cache
3.1 lru_cache提供的功能
3.2 cache的核心部件
3.3 my_cache的实现
4. lru_cache缓存和redis缓存的区别
5. 总结
本篇博客将结合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算法淘汰命中次数最少的缓存结果。缺点是没有办法对缓存过期时间进行设置。
来源:https://www.cnblogs.com/lifei01/p/14105346.html


猜你喜欢
- Object.freeze()Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这
- 本文实例讲述了Python图像处理之简单画板实现方法。分享给大家供大家参考,具体如下:Python图像处理也是依赖opencv的Python
- 在 CentOS7 中我们在安装 MySQL 的话会默认安装的是 MariaDB。它是一个在 MySQL 被收购之后,作者推出的应一个开源版
- 开始一个组件,毫无目的的写代码是一个不好的习惯,要经历 分析 => 抽象 => 实现 => 应用 四个阶段。组件DEMO地
- 如下所示:import cv2import osimport numpy as nproot_path = "I:/Images/
- 周六。据闻北服美女甚多,于是应邀去做了一个关于UED的讲座。人不多,讲的很乱,但大家听的很认真,欣慰。讲完之后回答了很多关于社区、搜索、设计
- admin组件使用Django 提供了基于 web 的管理工具。Django 自动管理工具是 django.contrib 的一部分。你可以
- 一、简介pandas中的ExcelFile()和ExcelWriter(),是pandas中对excel表格文件进行读写相关操作非常方便快捷
- 一个简单的tokenizer分词(tokenization)任务是Python字符串处理中最为常见任务了。我们这里讲解用正则表达式构建简单的
- split()介绍函数:split()Python中有split()和os.path.split()两个函数,具体作用如下:split():
- 主要实现的部分是利用NameGeneratorType读入系列图像,见头文件#include "itkNumericSeriesF
- 本文实例讲述了Mysql存储过程中游标的用法。分享给大家供大家参考。具体如下:1. 批量插入商户路由关联数据:DELIMITER $$USE
- 前言本文主要给大家介绍了关于Python利用元组代替字典并为元组元素命名的相关内容,下面话不多说了,来一起看看详细的介绍吧场景:一般使用字典
- 一:直接把MDB(MDE)文件放到网络中的共享目录中,在客户端做好对应的快捷方式二:数据库折分(菜单:工具,实用工具,折分)成前后台,把后台
- python的random库,提供了很多随机抽样方法。1. 设置随机数种子 seed()在适当的情形下,为例保证抽样的结果固定,不因多次运行
- 本文实例讲述了Python计算一个文件里字数的方法。分享给大家供大家参考。具体如下:这段程序从所给文件中找出字数来。from string
- 利用seek监控文件内容,并打印出变化内容:#/usr/bin/env python#-*- coding=utf-8 -*-pos = 0
- 压缩复制删除文件基于python语言怎么操作呢,压缩文件有四种格式:zip、rar、tar、tar.gz,在压缩过程中也容易出现很多问题,今
- 那什么时候会产生指令重排现象呢?两个阶段:1、编译期;2、运行期。编译期指令重排解释型语言是在运行期间执行编译+运行动作,所以运行效率较编译
- 先给大家展示下效果图: 核心代码如下所示:(‘.selectaddress').click(function () {//图一联系