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使用plotly绘制数据图表的方法
Jul 18 Python
Sanic框架蓝图用法实例分析
Jul 17 Python
python将一组数分成每3个一组的实例
Nov 14 Python
Python使用pandas对数据进行差分运算的方法
Dec 22 Python
python中下标和切片的使用方法解析
Aug 27 Python
使用PyTorch将文件夹下的图片分为训练集和验证集实例
Jan 08 Python
python 基于卡方值分箱算法的实现示例
Jul 17 Python
详解Selenium-webdriver绕开反爬虫机制的4种方法
Oct 28 Python
python os.rename实例用法详解
Dec 06 Python
Python 中 sorted 如何自定义比较逻辑
Feb 02 Python
python自动统计zabbix系统监控覆盖率的示例代码
Apr 03 Python
Python深度学习之Pytorch初步使用
May 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 得到根目录的 __FILE__ 常量
2008/07/23 PHP
PHP memcache扩展的三种安装方法
2009/04/26 PHP
php中eval函数的危害与正确禁用方法
2014/06/30 PHP
PHP图片处理之图片旋转和图片翻转实例
2014/11/19 PHP
PHP中str_split()函数的用法讲解
2019/04/11 PHP
laravel实现图片上传预览,及编辑时可更换图片,并实时变化的例子
2019/11/14 PHP
2020最新版 PhpStudy V8.1版本下载安装使用详解
2020/10/30 PHP
javascript Excel操作知识点
2009/04/24 Javascript
jquery库或JS文件在eclipse下报错问题解决方法
2014/04/17 Javascript
js动态删除div元素基本思路及实现代码
2014/05/08 Javascript
JS实现网页表格自动变大缩小的方法
2015/03/09 Javascript
javascript中html字符串转化为jquery dom对象的方法
2015/08/27 Javascript
基于MVC+EasyUI的web开发框架之使用云打印控件C-Lodop打印页面或套打报关运单信息
2016/08/29 Javascript
Javascript6中字符串的四个新用法分享
2016/09/11 Javascript
touch.js 拖动、缩放、旋转 (鼠标手势)功能代码
2017/02/04 Javascript
Vue Transition实现类原生组件跳转过渡动画的示例
2017/08/19 Javascript
在vue-cli中组件通信的方法
2017/12/16 Javascript
webpack 插件html-webpack-plugin的具体使用
2018/04/09 Javascript
优雅的将ElementUI表格变身成树形表格的方法步骤
2019/04/11 Javascript
详解从vue-loader源码分析CSS Scoped的实现
2019/09/23 Javascript
vue点击当前路由高亮小案例
2019/09/26 Javascript
基于javascript处理二进制图片流过程详解
2020/06/08 Javascript
基于Ionic3实现选项卡切换并重新加载echarts
2020/09/24 Javascript
Javascript执行上下文顺序的深入讲解
2020/11/04 Javascript
Python实现Kmeans聚类算法
2020/06/10 Python
在pandas多重索引multiIndex中选定指定索引的行方法
2018/11/16 Python
python命令行工具Click快速掌握
2019/07/04 Python
Python 文件操作之读取文件(read),文件指针与写入文件(write),文件打开方式示例
2019/09/29 Python
Python 中pandas索引切片读取数据缺失数据处理问题
2019/10/09 Python
python将unicode和str互相转化的实现
2020/05/11 Python
实例代码讲解Python 线程池
2020/08/24 Python
中专毕业生自荐信
2013/11/16 职场文书
党校个人自我鉴定范文
2014/03/28 职场文书
中国文明网向国旗敬礼活动精彩寄语2014
2014/09/27 职场文书
MySQL 全文检索的使用示例
2021/06/07 MySQL
SQL bool盲注和时间盲注详解
2022/07/23 SQL Server