Python实现LRU算法的2种方法


Posted in Python onJune 24, 2015

LRU:least recently used,最近最少使用算法。它的使用场景是:在有限的空间中存储对象时,当空间满时,会按一定的原则删除原有的对象,常用的原则(算法)有LRU,FIFO,LFU等。在计算机的Cache硬件,以及主存到虚拟内存的页面置换,还有Redis缓存系统中都用到了该算法。我在一次面试和一个笔试时,也遇到过这个问题。

LRU的算法是比较简单的,当对key进行访问时(一般有查询,更新,增加,在get()和set()两个方法中实现即可)时,将该key放到队列的最前端(或最后端)就行了,这样就实现了对key按其最后一次访问的时间降序(或升序)排列,当向空间中增加新对象时,如果空间满了,删除队尾(或队首)的对象。

在Python中,可以使用collections.OrderedDict很方便的实现LRU算法,当然,如果你想不到用OrderedDict,那可以用dict+list来实现。本文主要参考了LRU CACHE IN PYTHON,写的非常好,既实现了功能,又简洁易读。方法一的代码与参考文章基本相同,方法二是我自己想出来的,比较繁琐一些,其实OrderedDict本身也是类似的这种机制来实现的有序。

不过,下面的实现是有问题的,这个cache的key:value键值对中,value只能是不可变类型。因为,如果value是可变类型,那对于同一个key,所有调用get(key)方法返回的value都是指向同一个可变对象的,当修改其中一个value时,那所有的value都会被修改了,即使你没有调用set()方法也会这样。这是我们不希望看到的。解决方法我想到了两种,一是可变对象序列化后再存储,即将可变对象转为不可变对象;二是仍存储可变对象,但get()时,返回一个深拷贝,这样每个get()调用返回的对象就不会相互影响了。推荐第一种方法。另外,对于key,推荐使用str/unicode类型。

当并发时,还会存在一个问题,因为这涉及到对公共资源的写操作,所以必须要对set()加锁。其实,在并发情况下,所有对公共资源的写操作都要加锁。如果不存在并发的情况,只有单线程,那可以不加锁。

方法一:用OrderedDict实现(推荐)

from collections import OrderedDict

 

 

class LRUCache(OrderedDict):

    '''不能存储可变类型对象,不能并发访问set()'''
    def __init__(self,capacity):

        self.capacity = capacity

        self.cache = OrderedDict()

    
    def get(self,key):

        if self.cache.has_key(key):

            value = self.cache.pop(key)

            self.cache[key] = value

        else:

            value = None

         

        return value

    
    def set(self,key,value):

        if self.cache.has_key(key):

            value = self.cache.pop(key)

            self.cache[key] = value

        else:

            if len(self.cache) == self.capacity:

                self.cache.popitem(last = False)    #pop出第一个item

                self.cache[key] = value

            else:

                self.cache[key] = value

测试代码如下
c = LRUCache(5)

  

for i in range(5,10):

    c.set(i,10*i)

  

  

print c.cache, c.cache.keys()

  

c.get(5)

c.get(7)

  

print c.cache, c.cache.keys()

  

c.set(10,100)

print c.cache, c.cache.keys()

  

c.set(9,44)

print c.cache, c.cache.keys()

输出如下

OrderedDict([(5, 50), (6, 60), (7, 70), (8, 80), (9, 90)])     [5, 6, 7, 8, 9]

OrderedDict([(6, 60), (8, 80), (9, 90), (5, 50), (7, 70)])     [6, 8, 9, 5, 7]

OrderedDict([(8, 80), (9, 90), (5, 50), (7, 70), (10, 100)])   [8, 9, 5, 7, 10]

OrderedDict([(8, 80), (5, 50), (7, 70), (10, 100), (9, 90)])   [8, 5, 7, 10, 9]

方法二:用dict+list实现(不推荐)

class LRUCache(object):

    '''不能存储可变类型对象,不能并发访问set()'''

  

    def __init__(self,capacity):

        self.l = []

        self.d = {}

        self.capacity = capacity

         
    def get(self,key):

        if self.d.has_key(key):

            value = self.d[key]

            self.l.remove(key)

            self.l.insert(0,key)

        else:

            value = None

          

        return value

     
    def set(self,key,value):

        if self.d.has_key(key):

            self.l.remove(key)

        elif len(self.d) == self.capacity:

                oldest_key = self.l.pop()

                self.d.pop(oldest_key)

                  

        self.d[key] = value

        self.l.insert(0, key)

测试代码如下
c = LRUCache(5)

  

for i in range(5,10):

    c.set(i,10*i)

  

  

print c.d,c.l

  

c.get(5)

c.get(7)

  

print c.d,c.l

  

c.set(10,100)

print c.d,c.l

  

c.set(9,44)

print c.d,c.l

输出为

{8: 80, 9: 90, 5: 50, 6: 60, 7: 70}   [9, 8, 7, 6, 5]

{8: 80, 9: 90, 5: 50, 6: 60, 7: 70}   [7, 5, 9, 8, 6]

{5: 50, 7: 70, 8: 80, 9: 90, 10: 100} [10, 7, 5, 9, 8]

{5: 50, 7: 70, 8: 80, 9: 44, 10: 100} [9, 10, 7, 5, 8]
Python 相关文章推荐
Python不规范的日期字符串处理类
Jun 10 Python
python学习之面向对象【入门初级篇】
Jan 21 Python
Python实现动态图解析、合成与倒放
Jan 18 Python
python opencv捕获摄像头并显示内容的实现
Jul 11 Python
python matplotlib 画dataframe的时间序列图实例
Nov 20 Python
python 实现目录复制的三种小结
Dec 04 Python
python3实现在二叉树中找出和为某一值的所有路径(推荐)
Dec 26 Python
Numpy与Pytorch 矩阵操作方式
Dec 27 Python
Python如何通过Flask-Mail发送电子邮件
Jan 29 Python
关于TensorFlow新旧版本函数接口变化详解
Feb 10 Python
python3将变量写入SQL语句的实现方式
Mar 02 Python
pytorch 实现多个Dataloader同时训练
May 29 Python
Python中线程编程之threading模块的使用详解
Jun 23 #Python
Python Property属性的2种用法
Jun 21 #Python
Python中实现三目运算的方法
Jun 21 #Python
Python中有趣在__call__函数
Jun 21 #Python
Python的装饰器模式与面向切面编程详解
Jun 21 #Python
Python安装第三方库的3种方法
Jun 21 #Python
Python实现线程池代码分享
Jun 21 #Python
You might like
Warning: session_destroy() : Trying to destroy uninitialized sessionq错误
2011/06/16 PHP
php中经典方法实现判断多维数组是否为空
2011/10/23 PHP
19个超实用的PHP代码片段
2014/03/14 PHP
Thinkphp调用Image类生成缩略图的方法
2015/03/07 PHP
javascript 构建一个xmlhttp对象池合理创建和使用xmlhttp对象
2010/01/15 Javascript
javascript实现的一个随机点名功能
2014/08/26 Javascript
Jquery常用的方法汇总
2015/09/01 Javascript
jQuery zTree加载树形菜单功能
2016/02/25 Javascript
基于JavaScript实现幸运抽奖页面
2020/07/05 Javascript
Vue结合Video.js播放m3u8视频流的方法示例
2018/05/04 Javascript
微信小程序支付前端源码
2018/08/29 Javascript
微信小程序获取用户信息的两种方法wx.getUserInfo与open-data实例分析
2019/05/03 Javascript
Vue程序化的事件监听器(实例方案详解)
2020/01/07 Javascript
微信小程序淘宝首页双排图片布局排版代码(推荐)
2020/10/29 Javascript
Vue实现购物小球抛物线的方法实例
2020/11/22 Vue.js
python实现电子词典
2020/04/23 Python
python单线程实现多个定时器示例
2014/03/30 Python
Python开发WebService系列教程之REST,web.py,eurasia,Django
2014/06/30 Python
Python实现基于HTTP文件传输实例
2014/11/08 Python
Python简单的制作图片验证码实例
2017/05/31 Python
Python使用matplotlib绘制正弦和余弦曲线的方法示例
2018/01/06 Python
python3中的md5加密实例
2018/05/29 Python
python中正则表达式 re.findall 用法
2018/10/23 Python
python实现一行输入多个值和一行输出多个值的例子
2019/07/16 Python
pytorch 实现张量tensor,图片,CPU,GPU,数组等的转换
2020/01/13 Python
C# Debug和Testing相关面试题
2015/10/25 面试题
如何利用cmp命令比较文件
2013/09/23 面试题
房地产销售大学生自我评价分享
2013/11/11 职场文书
销售会计工作职责
2013/12/02 职场文书
迟到检讨书大全
2014/01/25 职场文书
十佳护士先进事迹
2014/05/08 职场文书
企业优秀团员事迹材料
2014/08/20 职场文书
小学教师求职信范文
2015/03/20 职场文书
人事任命通知书
2015/04/21 职场文书
劳保用品管理制度范本
2015/08/06 职场文书
教你使用Jenkins集成Harbor自动发布镜像
2022/04/03 Servers