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实现根据图标提取分类应用程序实例
Sep 28 Python
小米5s微信跳一跳小程序python源码
Jan 08 Python
Python装饰器原理与简单用法实例分析
Apr 29 Python
python爬虫正则表达式之处理换行符
Jun 08 Python
详解python单元测试框架unittest
Jul 02 Python
wxPython实现绘图小例子
Nov 19 Python
wxpython多线程防假死与线程间传递消息实例详解
Dec 13 Python
Python实现Canny及Hough算法代码实例解析
Aug 06 Python
Python 中如何写注释
Aug 28 Python
利用Python将图片中扭曲矩形的复原
Sep 07 Python
详解python的xlwings库读写excel操作总结
Feb 26 Python
Pytorch之扩充tensor的操作
Mar 04 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
php一些错误处理的方法与技巧总结
2013/08/10 PHP
Zend Framework入门知识点小结
2016/03/19 PHP
两个SUBMIT按钮,如何区分处理
2006/08/22 Javascript
JavaScript高级程序设计(第3版)学习笔记11 内建js对象
2012/10/11 Javascript
JavaScript中对象属性的添加和删除示例
2014/05/12 Javascript
Jquery给基本控件的取值、赋值示例
2014/05/23 Javascript
深入理解JavaScript系列(48):对象创建模式(下篇)
2015/03/04 Javascript
jquery实现图片放大镜功能
2015/11/23 Javascript
Bootstrap项目实战之首页内容介绍(全)
2016/04/25 Javascript
去除html代码里面的script正则方法
2016/05/19 Javascript
很棒的js选项卡切换效果
2016/07/15 Javascript
简单实现js浮动框
2016/12/13 Javascript
jQuery排序插件tableSorter使用方法
2017/02/10 Javascript
JQuery用$.ajax或$.getJSON跨域获取JSON数据的实现代码
2017/09/23 jQuery
JavaScript插件Tab选项卡效果
2017/11/14 Javascript
Vue 应用中结合vux使用微信 jssdk的方法
2018/08/28 Javascript
移动端H5页面返回并刷新页面(BFcache)的方法
2018/11/06 Javascript
使用 vue 实例更好的监听事件及vue实例的方法
2019/04/22 Javascript
探究Python的Tornado框架对子域名和泛域名的支持
2015/05/02 Python
python中执行shell的两种方法总结
2017/01/10 Python
Python三种遍历文件目录的方法实例代码
2018/01/19 Python
Python进阶之递归函数的用法及其示例
2018/01/31 Python
Python requests发送post请求的一些疑点
2018/05/20 Python
python之pymysql模块简单应用示例代码
2019/12/16 Python
Django多数据库配置及逆向生成model教程
2020/03/28 Python
Jack Rogers官网:美国经典的女性鞋靴品牌
2019/09/04 全球购物
英国领先的独立时装店:Van Mildert
2019/10/28 全球购物
奥地利时尚、美容、玩具和家居之家:Kastner & Öhler
2020/04/26 全球购物
Lucene推荐的分页方式是什么?
2015/12/07 面试题
电气工程及其自动化学生实习自我鉴定
2013/09/19 职场文书
2014年学校领导班子对照检查材料
2014/09/19 职场文书
2015年全国爱耳日活动总结
2015/02/27 职场文书
2015年女职工工作总结
2015/05/15 职场文书
2019新员工试用期转正申请书3篇
2019/08/13 职场文书
redis 限制内存使用大小的实现
2021/05/08 Redis
基于Redis延迟队列的实现代码
2021/05/13 Redis