Python实现LRU算法的2种方法

 更新时间:2015年06月24日 09:28:35   投稿:junjie  
这篇文章主要介绍了Python实现LRU算法的2种方法,本文分别给出了用OrderedDict实现、用dict+list实现两种方法,需要的朋友可以参考下

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]

相关文章

  • Pytorch之ToPILImage()不输出图片问题及解决

    Pytorch之ToPILImage()不输出图片问题及解决

    这篇文章主要介绍了Pytorch之ToPILImage()不输出图片问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-02-02
  • python变量前面加星(*)的含义及说明

    python变量前面加星(*)的含义及说明

    这篇文章主要介绍了python变量前面加星(*)的含义及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • Python matplotlib plotly绘制图表详解

    Python matplotlib plotly绘制图表详解

    plotly本身是个生态非常复杂的绘图工具,它对很多编程语言提供接口。交互式和美观易用应该是 Plotly 最大的优势,而 Matplotlib 的特点则是可定制化程度高,但语法也相对难学,各有优缺点。本文将通过示例详细讲解二者是如何绘制图表的,需要的可以参考一下
    2022-03-03
  • 机器学习10大经典算法详解

    机器学习10大经典算法详解

    这篇文章主要为大家详细介绍了机器学习10大经典算法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • Python标准库之itertools库的使用方法

    Python标准库之itertools库的使用方法

    Python提供了一个非常棒的模块用于创建自定义的迭代器,这个模块就是 itertools。itertools 提供的工具相当高效且节省内存,下面这篇文章主要给大家介绍了关于Python标准库之itertools库使用的相关资料,需要的朋友可以参考下。
    2017-09-09
  • pandas 数据实现行间计算的方法

    pandas 数据实现行间计算的方法

    今天小编就为大家分享一篇pandas 数据实现行间计算的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-06-06
  • python遍历文件夹,指定遍历深度与忽略目录的方法

    python遍历文件夹,指定遍历深度与忽略目录的方法

    今天小编就为大家分享一篇python遍历文件夹,指定遍历深度与忽略目录的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • python GUI库图形界面开发之PyQt5布局控件QHBoxLayout详细使用方法与实例

    python GUI库图形界面开发之PyQt5布局控件QHBoxLayout详细使用方法与实例

    这篇文章主要介绍了python GUI库图形界面开发之PyQt5布局控件QHBoxLayout详细使用方法与实例,需要的朋友可以参考下
    2020-03-03
  • 基于Python实现图片主题色的提取

    基于Python实现图片主题色的提取

    在 Groove 音乐中,当我们改变歌曲时,底部播放栏的颜色会随专辑封面而变,这就是提取了主题色。本文将利用Python实现图片主题色的提取,感兴趣的可以了解一下
    2022-03-03
  • Python selenium的这三种等待方式一定要会!

    Python selenium的这三种等待方式一定要会!

    今天给大家带来的是关于Python的相关知识,文章围绕着selenium三种等待方式展开,文中有非常详细的介绍,需要的朋友可以参考下
    2021-06-06

最新评论