Python 如何手动编写一个自己的LRU缓存装饰器的方法实现
作者:蠢萌的二狗子 发布时间:2022-08-04 13:06:10
标签:Python,LRU,缓存装饰器
LRU缓存算法,指的是近期最少使用算法,大体逻辑就是淘汰最长时间没有用的那个缓存,这里我们使用有序字典,来实现自己的LRU缓存算法,并将其包装成一个装饰器。
1、首先创建一个my_cache.py文件 编写自己我们自己的LRU缓存算法,代码如下:
import time
from collections import OrderedDict
'''
基于LRU,近期最少用缓存算法写的装饰器。
'''
class LRUCacheDict:
def __init__(self, max_size=1024, expiration=60):
self.max_size = max_size
self.expiration = expiration
self._cache = {}
self._access_records = OrderedDict() # 记录访问时间
self._expire_records = OrderedDict() # 记录失效时间
def __setitem__(self, key, value): # 设置缓存
now = int(time.time())
self.__delete__(key) # 删除原有使用该Key的所有缓存
self._cache[key] = value
self._access_records = now # 设置访问时间
self._expire_records = now + self.expiration # 设置过期时间
self.cleanup()
def __getitem__(self, key): # 更新缓存
now = int(time.time())
del self._access_records[key] # 删除原有的访问时按
self._access_records[key] = now
self.cleanup()
def __contains__(self, key): # 这个是字典默认调用key的方法
self.cleanup()
return key in self._cache
def __delete__(self, key):
if key in self._cache:
del self._cache[key] # 删除缓存
del self._access_records[key] # 删除访问时间
del self._expire_records[key] # 删除过期时间
def cleanup(self): # 用于去掉无效(超过大小)和过期的缓存
if self._expire_records is None:
return None
pending_delete_keys = []
now = int(time.time())
for k, v in self._expire_records.items(): # 判断缓存是否失效
if v < now:
pending_delete_keys.append(k)
for del_k in pending_delete_keys:
self.__delete__(del_k)
while len(self._cache) > self.max_size: # 判断缓存是否超过长度
for k in self._access_records.keys(): # LRU 是在这里实现的,如果缓存用的最少,那么它存入在有序字典中的位置也就最前
self.__delete__(k)
break
代码逻辑其实很简单,上面的注释已经很详细了,不懂的话多看几次。这里实现LRU逻辑的其实是有序字典OrderedDict,你最先存入的值就会存在字典的最前面。当一个值使用时候,我们会重新储存过期时间,导致被经常使用的缓存,会存在字典的后面。而一但缓存的内容长度超过限制时候,这里会调用有序字典最前面的key(也即是近期相对用的最少的),并删除对应的内容,以达到LRU的逻辑。
2、在将我们写好的算法改成装饰器:
from functools import wraps
from my_cache import LRUCacheDict
def lru_cache(max_size=1024, expiration=60, types='LRU'):
if types == 'lru' or types == 'LRU':
my_cache = LRUCacheDict(max_size=max_size, expiration=expiration)
def wrapper(func):
@wraps(func)
def inner(*args, **kwargs):
key = repr(*args, **kwargs)
try:
result = my_cache[key]
except KeyError:
result = func(*args, **kwargs)
my_cache[key] = result
return result
return inner
return wrapper
这里需要解释的是直接使用 my_cache[key],这个类似字典的方法,实际上是调用了 LRUCacheDict 中的 __contations__方法,这也是字典中实现通过key取值的方法。这个装饰器里,我加入了types的参数,你们可以根据需求,实现不同的缓存算法,丰富这个装饰器的功能,而lru缓存本身,其实已经是python的标准库了,可以引入functools.lru_cache来调用。
来源:https://blog.csdn.net/qq442000755/article/details/122150012


猜你喜欢
- 昨天解决完数据库跨权限操作后,今天打开项目突然报错了,然后重启数据库时,报 ERROR! MySQL server PID file cou
- 超级简单实现iframe框架滚动控制,前提要会简单修改原代码。step1:插入iframe标签在你想要的位置。<iframe 
- 本文实例讲述了Python操作mongodb数据库进行模糊查询操作。分享给大家供大家参考,具体如下:# -*- coding: utf-8
- 传递函数创建传递函数有两种方式:import control as ctrl# 方式 1s = ctrl.tf('s')sy
- 前言这是一个使用HttpRunner开发接口平台的简单Demo。新建Django项目安装依赖包pip install httprunner=
- 线程是进程中可以调度执行的实体。而且,它是操作系统中可以执行的最小处理单元。简单地说,一个线程就是一个程序中可以独立于其他代码执行的指令序列
- 前言手里有一点点公司的股票, 拿不准在什么时机抛售, 程序员也没时间天天盯着看,不如动手写个小程序, 把股票趋势每天早上发到邮箱里,用 py
- 本篇文章介绍如何用OpenCV Python来计算直方图,并简略介绍用NumPy和Matplotlib计算和绘制直方图直方图的背景知识、用途
- 一、变量的定义程序中,数据都是临时存储在内存中,为了更快速的查找或使用这个数据,通常我们把这个数据在内存中存储之后,给整个数据定义一个名称,
- 本文实例为大家分享了python图书管理系统的具体代码,供大家参考,具体内容如下实现语言:python图形框架:DTK+2.0数据库框架:S
- 了解SQL Server 2005数据库的朋友可能都知道,tempdb系统数据库是一个全局资源,可供连接到SQL Server 2005实例
- Xajax是PHP一个不用刷新或者跳到其他页面,就能通过点击组件等与后台后台数据库交互的技术Xajax是php的一个插件,要想使用Xajax
- 如下例data2[‘营业成本率'] = data2[‘营业成本本年累计']/data2[‘营业收入本年累计']*10
- python开发者向普通windows用户分享程序,要给程序加图形化的界面(传送门:这可能是最好玩的python GUI入门实例! http
- 本文实例讲述了Python获取任意xml节点值的方法。分享给大家供大家参考。具体实现方法如下:# -*- coding: utf-8 -*-
- 一、语法错误异常:大多数的异常都不会被程序处理,都以错误信息的形式展现在这里二、异常处理while True: t
- 前言前文讲述了ppi-cpi和m0-m1-m2的图形绘制,在本文中继续分享一个反映经济活动景气度的指标PMI,在本文中还是采用爬虫的方式获取
- # -*- coding: cp936 -*-import socketfrom threading import Thread,activ
- MySQL的分页似乎一直是个问题,有什么优化方法吗?网上看到网上推荐了一些分页方法,但似乎不太可行,你能点评一下吗?方法1: 直接使用数据库
- VBScript似乎已经成为ASP服务器端开发的首先语言,VBScript函数库丰富、而且使用起来也很容易上手,即使平时不太编程的朋友,只要