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 测试实现方法
Dec 24 Python
python使用nntp读取新闻组内容的方法
May 08 Python
Python 获得13位unix时间戳的方法
Oct 20 Python
Python设计模式之策略模式实例详解
Jan 21 Python
centos6.5安装python3.7.1之后无法使用pip的解决方案
Feb 14 Python
python解析xml简单示例
Jun 21 Python
pip安装python库的方法总结
Aug 02 Python
python实现简单反弹球游戏
Apr 12 Python
教你使用Pandas直接核算Excel中快递费用
May 12 Python
写好Python代码的几条重要技巧
May 21 Python
Python爬虫基础初探selenium
May 31 Python
python图片灰度化处理的几种方法
Jun 23 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
天津市收音机工业发展史
2021/03/04 无线电
php摘要生成函数(无乱码)
2012/02/04 PHP
如何让thinkphp在模型中自动完成session赋值小教程
2014/09/05 PHP
php计算多维数组中所有值总和的方法
2015/06/24 PHP
php+jQuery ajax实现的实时刷新显示数据功能示例
2019/09/12 PHP
javaScript Array(数组)相关方法简述
2009/07/25 Javascript
jQuery控制TR显示隐藏的三种常用方法
2014/08/21 Javascript
对比分析json及XML
2014/11/28 Javascript
jQuery打字效果实现方法(附demo源码下载)
2015/12/18 Javascript
使用Promise解决多层异步调用的简单学习心得
2016/05/17 Javascript
jQuery使用中可能被XSS攻击的一些危险环节提醒
2016/05/24 Javascript
jQuery中Find选择器用法示例
2016/09/21 Javascript
jquery实现异步加载图片(懒加载图片一种方式)
2017/04/24 jQuery
NodeJS爬虫实例之糗事百科
2017/12/14 NodeJs
Vue中使用vux配置代码详解
2018/09/16 Javascript
Vue 实现对quill-editor组件中的工具栏添加title
2020/08/03 Javascript
[03:46]DOTA2英雄基础教程 维萨吉
2013/12/11 DOTA
[01:12](回顾)DOTA2国际邀请赛,全世界DOTAer的盛宴
2014/07/01 DOTA
基于Python 装饰器装饰类中的方法实例
2018/04/21 Python
纯python进行矩阵的相乘运算的方法示例
2019/07/17 Python
与Django结合利用模型对上传图片预测的实例详解
2019/08/07 Python
使用python实现希尔、计数、基数基础排序的代码
2019/12/25 Python
Python 实现一行输入多个数字(用空格隔开)
2020/04/29 Python
Python如何使用神经网络进行简单文本分类
2021/02/25 Python
详解如何用canvas画一个微笑的表情
2019/03/14 HTML / CSS
如何设置Java的运行环境
2013/04/05 面试题
建筑工程技术应届生自荐信
2013/09/27 职场文书
手机业务员岗位职责
2013/12/13 职场文书
护士自我鉴定怎么写
2014/02/07 职场文书
企业委托书范本
2014/09/13 职场文书
大学生自我评价200字(4篇)
2014/09/17 职场文书
大学生入党自荐书
2015/03/05 职场文书
毕业论文致谢格式模板
2015/05/14 职场文书
教师正风肃纪心得体会
2016/01/15 职场文书
MySQL 表空间碎片的概念及相关问题解决
2021/05/07 MySQL
关于windows server 2012 DC 环境 重启后蓝屏代码:0xc00002e2的问题
2022/05/25 Servers