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 相关文章推荐
Python3基础之list列表实例解析
Aug 13 Python
Python中使用Tkinter模块创建GUI程序实例
Jan 14 Python
python中字典dict常用操作方法实例总结
Apr 04 Python
浅析Python的web.py框架中url的设定方法
Jul 11 Python
Django之创建引擎索引报错及解决详解
Jul 17 Python
Python操作SQLite数据库过程解析
Sep 02 Python
Pytorch Tensor基本数学运算详解
Dec 30 Python
Python StringIO如何在内存中读写str
Jan 07 Python
Python使用pyyaml模块处理yaml数据
Apr 14 Python
python使用建议与技巧分享(一)
Aug 17 Python
PyTorch如何搭建一个简单的网络
Aug 24 Python
手把手教你实现PyTorch的MNIST数据集
Jun 28 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中基于ts与nts版本- vc6和vc9编译版本的区别详解
2013/04/26 PHP
最准确的php截取字符串长度函数
2015/10/29 PHP
PHP使用preg_split和explode分割textarea存放内容的方法分析
2017/07/03 PHP
Yii框架学习笔记之session与cookie简单操作示例
2019/04/30 PHP
在jquery中combobox多选的不兼容问题总结
2013/12/24 Javascript
js实现鼠标移到链接文字弹出一个提示层的方法
2015/05/11 Javascript
jquery实现点击label的同时触发文本框点击事件的方法
2015/06/05 Javascript
AngularJS表单验证中级篇(3)
2016/09/28 Javascript
详解JavaScript对象的深浅复制
2017/03/30 Javascript
Es6 Generator函数详细解析
2018/02/24 Javascript
详解浏览器缓存和webpack缓存配置
2018/07/06 Javascript
webpack4 配置 ssr 环境遇到“document is not defined”
2019/10/24 Javascript
jQuery实现视频展示效果
2020/05/30 jQuery
[05:13]TI4 中国战队 机场出征!!
2014/07/07 DOTA
[00:29]2019完美世界全国高校联赛(秋季赛)总决赛海口落幕
2019/12/10 DOTA
利用Anaconda完美解决Python 2与python 3的共存问题
2017/05/25 Python
Python2/3中urllib库的一些常见用法
2017/12/19 Python
pandas DataFrame实现几列数据合并成为新的一列方法
2018/06/08 Python
详解numpy矩阵的创建与数据类型
2019/10/18 Python
python pyinstaller打包exe报错的解决方法
2019/11/02 Python
Python爬虫爬取煎蛋网图片代码实例
2019/12/16 Python
通过Turtle库在Python中绘制一个鼠年福鼠
2020/02/03 Python
Python如何爬取b站热门视频并导入Excel
2020/08/10 Python
python 将列表里的字典元素合并为一个字典实例
2020/09/01 Python
pycharm 快速解决python代码冲突的问题
2021/01/15 Python
英国曼彻斯特宠物用品品牌:Bunty Pet Products
2019/07/27 全球购物
毕业生的自我鉴定该怎么写
2013/12/02 职场文书
祖国在我心中演讲稿300字
2014/05/04 职场文书
廉洁教育学习材料
2014/05/19 职场文书
创先争优标语
2014/06/27 职场文书
群众路线个人整改方案
2014/10/25 职场文书
优秀大学生自荐信
2015/03/26 职场文书
简单实现一个手持弹幕功能+文字抖动特效
2021/03/31 HTML / CSS
Pyhton模块和包相关知识总结
2021/05/12 Python
python 爬取哔哩哔哩up主信息和投稿视频
2021/06/07 Python
docker-compose部署Yapi的方法
2022/04/08 Servers