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创建文件和追加文件内容实例
Oct 21 Python
Python爬取网易云音乐热门评论
Mar 31 Python
python3将视频流保存为本地视频文件
Jun 20 Python
python实现求两个字符串的最长公共子串方法
Jul 20 Python
django 自定义过滤器的实现
Feb 26 Python
python五子棋游戏的设计与实现
Jun 18 Python
Pycharm保存不能自动同步到远程服务器的解决方法
Jun 27 Python
Python 计算任意两向量之间的夹角方法
Jul 05 Python
python 列表推导式使用详解
Aug 29 Python
python redis 批量设置过期key过程解析
Nov 26 Python
在django admin详情表单显示中添加自定义控件的实现
Mar 11 Python
Tensorflow安装问题: Could not find a version that satisfies the requirement tensorflow
Apr 20 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 前一天或后一天的日期
2008/06/28 PHP
ThinkPHP模板标签eq if 中区分0,null,false的方法
2017/03/24 PHP
老生常谈PHP面向对象之解释器模式
2017/05/17 PHP
PHP5.6新增加的可变函数参数用法分析
2017/08/25 PHP
phpstorm 正则匹配删除空行、注释行(替换注释行为空行)
2018/01/21 PHP
用Javascript做flash做的事..才完成的一个类.Auntion Action var 0.1
2007/02/23 Javascript
JavaScript类和继承 prototype属性
2010/09/03 Javascript
JS动态创建Table,Tr,Td并赋值的具体实现
2013/07/05 Javascript
购物车选中得到价格实现示例
2014/01/26 Javascript
JavaScript实现带箭头标识的多级下拉菜单效果
2015/08/27 Javascript
angularjs在ng-repeat中使用ng-model遇到的问题
2016/01/21 Javascript
详解AngularJS控制器的使用
2016/03/09 Javascript
Javascript中判断一个值是否为undefined的方法详解
2016/09/28 Javascript
jQuery弹出层插件popShow(改进版)用法示例
2017/01/23 Javascript
vue实现的上传图片到数据库并显示到页面功能示例
2018/03/17 Javascript
详解react内联样式使用webpack将px转rem
2018/09/13 Javascript
vue.js自定义组件directives的实例代码
2018/11/09 Javascript
解决vue自定义指令导致的内存泄漏问题
2020/08/04 Javascript
vue实现在进行增删改操作后刷新页面
2020/08/05 Javascript
python执行shell获取硬件参数写入mysql的方法
2014/12/29 Python
Python中特殊函数集锦
2015/07/27 Python
利用Python脚本实现ping百度和google的方法
2017/01/24 Python
python中利用xml.dom模块解析xml的方法教程
2017/05/24 Python
Python排序算法实例代码
2017/08/10 Python
Python 由字符串函数名得到对应的函数(实例讲解)
2017/08/10 Python
Python Web程序搭建简单的Web服务器
2019/07/31 Python
Scrapy基于scrapy_redis实现分布式爬虫部署的示例
2020/09/29 Python
Python如何使用vars返回对象的属性列表
2020/10/17 Python
CSS3 制作绽放的莲花采用效果叠加实现
2013/01/31 HTML / CSS
澳洲小众品牌的集合网站:BNKR
2018/02/23 全球购物
护理毕业生自荐信范文
2013/12/22 职场文书
网上卖盒饭创业计划书
2014/01/26 职场文书
2015年党务工作者个人工作总结
2015/10/22 职场文书
使用springboot暴露oracle数据接口的问题
2021/05/07 Oracle
Python基础之元组与文件知识总结
2021/05/19 Python
MySQL中order by的使用详情
2021/11/17 MySQL