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生成指定尺寸缩略图的示例
May 07 Python
Python爬虫抓取代理IP并检验可用性的实例
May 07 Python
解决python升级引起的pip执行错误的问题
Jun 12 Python
Python格式化输出字符串方法小结【%与format】
Oct 29 Python
Python使用pyserial进行串口通信的实例
Jul 02 Python
Python获取时间范围内日期列表和周列表的函数
Aug 05 Python
python集合的创建、添加及删除操作示例
Oct 08 Python
Python3 字典dictionary入门基础附实例
Feb 10 Python
Python中的X[:,0]、X[:,1]、X[:,:,0]、X[:,:,1]、X[:,m:n]和X[:,:,m:n]
Feb 13 Python
利用Python实现斐波那契数列的方法实例
Jul 26 Python
python的数学算法函数及公式用法
Nov 18 Python
超详细Python解释器新手安装教程
May 10 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使用cookie保存登录用户名的方法
2015/01/26 PHP
PHP按指定键值对二维数组进行排序的方法
2015/12/22 PHP
PHP检测用户是否关闭浏览器的方法
2016/02/14 PHP
php微信公众平台开发(一) 配置接口
2016/12/06 PHP
Laravel5.4框架使用socialite实现github登录的方法
2019/03/20 PHP
ThinkPHP 5 AJAX跨域请求头设置实现过程解析
2020/10/28 PHP
JavaScript库 开发规则
2009/01/31 Javascript
jQuery1.3.2 升级到jQuery1.4.4需要修改的地方
2011/01/06 Javascript
SeaJS入门教程系列之SeaJS介绍(一)
2014/03/03 Javascript
js简单的表格添加行和删除行操作示例
2014/03/31 Javascript
JS日程管理插件FullCalendar简单实例
2017/02/07 Javascript
基于node下的http小爬虫的示例代码
2018/01/11 Javascript
使用Vue开发动态刷新Echarts组件的教程详解
2018/03/22 Javascript
Node.Js生成比特币地址代码解析
2018/04/21 Javascript
mpvue跳转页面及注意事项
2018/08/03 Javascript
vue 属性拦截实现双向绑定的实例代码
2018/10/24 Javascript
面试题:react和vue的区别分析
2019/04/08 Javascript
快速搭建Node.js(Express)用户注册、登录以及授权的方法
2019/05/09 Javascript
VUE 直接通过JS 修改html对象的值导致没有更新到数据中解决方法分析
2019/12/02 Javascript
Vue3不支持Filters过滤器的问题
2020/09/24 Javascript
[01:38]【DOTA2亚洲邀请赛】Sumail——梦开始的地方
2017/03/03 DOTA
Python下使用Scrapy爬取网页内容的实例
2018/05/21 Python
python3实现爬取淘宝美食代码分享
2018/09/23 Python
Django中Middleware中的函数详解
2019/07/18 Python
tensorflow如何批量读取图片
2019/08/29 Python
Python 解决火狐浏览器不弹出下载框直接下载的问题
2020/03/09 Python
美国照明、家居装饰和家具购物网站:Bellacor
2017/09/20 全球购物
乌克兰在线电子产品商店:MTA
2019/11/14 全球购物
软件测试工程师结构化面试题库
2016/11/23 面试题
最新的咖啡店创业计划书
2013/12/30 职场文书
2014党支部对照检查材料思想汇报
2014/10/05 职场文书
2015年乡镇统计工作总结
2015/04/22 职场文书
残联2016年全国助残日活动总结
2016/04/01 职场文书
《悬崖边的树》读后感2篇
2019/12/02 职场文书
python3美化表格数据输出结果的实现代码
2021/04/14 Python
python实现剪贴板的操作
2021/07/01 Python