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
来实现如上效果,利用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
函数
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) 时,并没有真正执行函数体,而是直接返回缓存的结果