python基础-LruCaChe

      • 常规方法
      • OrderedDict
      • lru_cache

LRU是Least Recently Used 近期最少使用算法。是一种置换算法,
百度百科是这么介绍的:内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据哪些数据属于LRU而将其移出内存而腾出空间来加载另外的数据。

LruCache算法在安卓开发里面用的很广泛

在Android中的应用:Android提供了LruCache 类来 实现Android缓存的lru算法;

在Android中显示单个图片,加载单个网络图片是比较简单的,而多个图片同时加载时,就会变的比较复杂,比如ListView,GridView等控件,一般处理的方式有多线程加载(使用Thead + handle)的方式,还有异步加载(AsyncTask)。要提高体验度可以使用Lru算法,做缓存的处理

今天我们就来学习下python下的LruCache是如何实现的,在functools模块中lru_cache函数,在学习lru_cache之前我们先自己实现一个类似的算法,来了解下lru_cache算法

需求:构建一个字典,长度为5,每个元素都有添加的时间,我们往字典里面添加6个元素,由于超出规定的5个元素,我们就要删除字典里面的最先添加的元素,也就是时间值,用这个时间值来代表近期最少使用的元素
先把代码贴出来,然后在简单分析下

常规方法

import datetime
import random
import time

class MyLruCache:
    def __init__(self):
        self.cache = {}
        self.max_cache_size = 5

    def __contains__(self, key):
        return  key in self.cache

    def get_cache_size(self):
        return self.max_cache_size

    #删除最早的条目
    def update_cache(self,key,value):
        #当缓存满了,需要添加新东西就移除最早的
        if key not in self.cache and self.max_cache_size<= len(self.cache):
                self.remove_oldest()
        self.cache[key] = {"date":time.time(),"val":value}
    #找到一个字典里面最早的数据
    def remove_oldest(self):
        oldest=None
        for k in self.cache:
            if not oldest:
                oldest = k
            elif self.cache[k]["date"] < self.cache[oldest]["date"]:
                oldest = k
        self.cache.pop(oldest)

if __name__ == "__main__":
    keys = ['test', 'red', 'fox', 'fence', 'junk', 'other']
    cache = MyLruCache()
    for i, key in enumerate(keys):
        if key in cache:
            continue
        else:
            value = key
            time.sleep(1)
            cache.update_cache(key, value)

    for i in cache.cache.items():
        print(i)

输出如下:

('red', {'date': 1522487264.2250743, 'val': 'red'})
('fox', {'date': 1522487265.2260435, 'val': 'fox'})
('fence', {'date': 1522487266.2265983, 'val': 'fence'})
('junk', {'date': 1522487267.227419, 'val': 'junk'})
('other', {'date': 1522487268.2276285, 'val': 'other'})

通过self.max_cache_size = 5设定字典容量,在update_cache更新字典容器,
key not in self.cache and self.max_cache_size<= len(self.cache)时候,也就是目前存放的元素没有在字典内,并且字典的设定容量值<=目前构建的字典的容量值,就进行删除最先添加的元素,如果不符合该条件,就往字典添加数据元素
remove_oldest方法中,去循环遍厉,获取当前字典最先添加的元素,并且self.cache.pop(oldest)删除掉
最后在__main__中进行测试,字典中的元素格式如下

('red', {'date': 1522487264.2250743, 'val': 'red'})

构建keys = ['test', 'red', 'fox', 'fence', 'junk', 'other']数据,然后构建需要的数据格式,然后循环添加即可

最后我们看输出结果如下:

('red', {'date': 1522487264.2250743, 'val': 'red'})
('fox', {'date': 1522487265.2260435, 'val': 'fox'})
('fence', {'date': 1522487266.2265983, 'val': 'fence'})
('junk', {'date': 1522487267.227419, 'val': 'junk'})
('other', {'date': 1522487268.2276285, 'val': 'other'})

从结果就看出最后把keys = ['test', 'red', 'fox', 'fence', 'junk', 'other']中的第一个添加的test构建的数据给删除掉了,继而添加了后面数据

OrderedDict

我们也可以通过OrderedDict来实现如上效果,利用collection.OrderedDict数据类型实现最近最少使用算法OrderedDict有个特殊方法popitem(Last=False)时则实现队列,弹出最先插入的元素,
而当Last=True则实现堆栈方法,弹出的是最近插入的那个元素

我们来修改下上述代码

import datetime
import random
import time
from collections import OrderedDict
class MyLruCache:
    def __init__(self):
        self.cache = OrderedDict()
        self.max_cache_size = 5

    def __contains__(self, key):
        return  key in self.cache

    def get_cache_size(self):
        return self.max_cache_size

    #删除最早的条目
    def update_cache(self,key,value):
        #当缓存满了,需要添加新东西就移除最早的
        if key not in self.cache and self.max_cache_size<= len(self.cache):
                self.cache.popitem(last=False)

        self.cache[key] = {"date":time.time(),"val":value}

if __name__ == "__main__":
    keys = ['test', 'red', 'fox', 'fence', 'junk', 'other']
    cache = MyLruCache()
    for i, key in enumerate(keys):
        if key in cache:
            continue
        else:
            value = key
            time.sleep(1)
            cache.update_cache(key, value)

    for i in cache.cache.items():
        print(i)

最后输出结果一样,删除了最先添加的数据元素

('red', {'date': 1522489200.1806178, 'val': 'red'})
('fox', {'date': 1522489201.1806552, 'val': 'fox'})
('fence', {'date': 1522489202.18121, 'val': 'fence'})
('junk', {'date': 1522489203.181699, 'val': 'junk'})
('other', {'date': 1522489204.181741, 'val': 'other'})

我们只是修改了update_cache代码如下:

  #删除最早的条目
    def update_cache(self,key,value):
        #当缓存满了,需要添加新东西就移除最早的
        if key not in self.cache and self.max_cache_size<= len(self.cache):
                self.cache.popitem(last=False)

        self.cache[key] = {"date":time.time(),"val":value}

以上就是LruCache的简单理念,接下来我们就看下python标准库的lru_cache函数

lru_cache

from functools import lru_cache
@lru_cache(None)
def add(x, y):
    print("calculating: %s + %s" % (x, y))
    return x + y

print(add(1, 2))
print(add(1, 2))
print(add(2, 3))

输出如下:

calculating: 1 + 2
3
3
calculating: 2 + 3
5

从结果可以看出,当第二次调用 add(1, 2) 时,并没有真正执行函数体,而是直接返回缓存的结果

你可能感兴趣的:(Python基础)