Python实现的一个简单LRU cache

yipeiwu_com6年前Python基础

起因:我的同事需要一个固定大小的cache,如果记录在cache中,直接从cache中读取,否则从数据库中读取。python的dict 是一个非常简单的cache,但是由于数据量很大,内存很可能增长的过大,因此需要限定记录数,并用LRU算法丢弃旧记录。key 是整型,value是10KB左右的python对象

分析:

1)可以想到,在对于cache,我们需要维护 key -> value 的关系

2)而为了实现LRU,我们又需要一个基于时间的优先级队列,来维护   timestamp  -> (key, value) 的关系

3)当cache 中的记录数达到一个上界maxsize时,需要将timestamp 最小的(key,value) 出队列

4) 当一个(key, value) 被命中时,实际上我们需要将它从队列中,移除并插入到队列的尾部。

从分析可以看出我们的cache 要达到性能最优需要满足上面的四项功能,对于队表的快速移除和插入,链表显然是最优的选择,为了快速移除,最好使用双向链表,为了插入尾部,需要有指向尾部的指针。

下面用python 来实现:

复制代码 代码如下:

#encoding=utf-8

class LRUCache(object):
    def __init__(self, maxsize):
        # cache 的最大记录数
        self.maxsize = maxsize
        # 用于真实的存储数据
        self.inner_dd = {}
        # 链表-头指针
        self.head = None
        # 链表-尾指针
        self.tail = None

    def set(self, key, value):
        # 达到指定大小     
        if len(self.inner_dd) >= self.maxsize:
            self.remove_head_node()

        node = Node()
        node.data = (key, value)
        self.insert_to_tail(node)
        self.inner_dd[key] = node

    def insert_to_tail(self, node):
        if self.tail is None:
            self.tail = node
            self.head = node
        else:
            self.tail.next = node
            node.pre = self.tail
            self.tail = node

    def remove_head_node(self):
        node = self.head
        del self.inner_dd[node.data[0]]
        node = None
        self.head = self.head.next
        self.head.pre = None
    def get(self, key):
        if key in self.inner_dd:
            # 如果命中, 需要将对应的节点移动到队列的尾部
            node = self.inner_dd.get(key)
            self.move_to_tail(node)
            return node.data[1]
        return None

    def move_to_tail(self, node):
        # 只需处理在队列头部和中间的情况
        if not (node == self.tail):
            if node == self.head:
                self.head = node.next
                self.head.pre = None
                self.tail.next = node
                node.pre = self.tail
                node.next = None
                self.tail = node
            else:
                pre_node = node.pre
                next_node = node.next
                pre_node.next = next_node
                next_node.pre = pre_node

                self.tail.next = node
                node.pre = self.tail
                node.next = None
                self.tail = node

class Node(object):
    def __init__(self):
        self.pre = None
        self.next = None
        # (key, value)
        self.data = None

    def __eq__(self, other):
        if self.data[0] == other.data[0]:
            return True
        return False
    def __str__(self):
       return str(self.data)

if __name__ == '__main__':
    cache = LRUCache(10)
    for i in xrange(1000):
        cache.set(i, i+1)
        cache.get(2)
    for key in cache.inner_dd:
        print key, cache.inner_dd[key]

相关文章

Python使用PDFMiner解析PDF代码实例

Python使用PDFMiner解析PDF代码实例

近期在做爬虫时有时会遇到网站只提供pdf的情况,这样就不能使用scrapy直接抓取页面内容了,只能通过解析PDF的方式处理,目前的解决方案大致只有pyPDF和PDFMiner。因为据说P...

Python multiprocess pool模块报错pickling error问题解决方法分析

本文实例讲述了Python multiprocess pool模块报错pickling error问题解决方法。分享给大家供大家参考,具体如下: 问题 之前在调用class内的函数用mu...

python实现定时压缩指定文件夹发送邮件

工作中每天需要收集部门内的FR文件,发送给外部部门的同事帮忙上传,这么发了有大半年,昨天亮光一闪,为什么不做成自动化呢,于是用python实现了整个流程,今天体验了一下真是美滋滋。 代码...

python 处理微信对账单数据的实例代码

下面一段代码给大家介绍python 处理微信对账单数据,具体代码如下所示: #下载对账单并存储到数据库 @app.route("/bill/<string:date>",...

用Python实现斐波那契(Fibonacci)函数

Fibonacci斐波那契数列,很简单,就是一个递归嘛,学任何编程语言可能都会做一下这个。 最近在玩Python,在粗略的看了一下Learning Python和Core Python之...