Python實(shí)現(xiàn)LRU算法的2種方法
LRU:least recently used,最近最少使用算法。它的使用場景是:在有限的空間中存儲對象時,當(dāng)空間滿時,會按一定的原則刪除原有的對象,常用的原則(算法)有LRU,F(xiàn)IFO,LFU等。在計(jì)算機(jī)的Cache硬件,以及主存到虛擬內(nèi)存的頁面置換,還有Redis緩存系統(tǒng)中都用到了該算法。我在一次面試和一個筆試時,也遇到過這個問題。
LRU的算法是比較簡單的,當(dāng)對key進(jìn)行訪問時(一般有查詢,更新,增加,在get()和set()兩個方法中實(shí)現(xiàn)即可)時,將該key放到隊(duì)列的最前端(或最后端)就行了,這樣就實(shí)現(xiàn)了對key按其最后一次訪問的時間降序(或升序)排列,當(dāng)向空間中增加新對象時,如果空間滿了,刪除隊(duì)尾(或隊(duì)首)的對象。
在Python中,可以使用collections.OrderedDict很方便的實(shí)現(xiàn)LRU算法,當(dāng)然,如果你想不到用OrderedDict,那可以用dict+list來實(shí)現(xiàn)。本文主要參考了LRU CACHE IN PYTHON,寫的非常好,既實(shí)現(xiàn)了功能,又簡潔易讀。方法一的代碼與參考文章基本相同,方法二是我自己想出來的,比較繁瑣一些,其實(shí)OrderedDict本身也是類似的這種機(jī)制來實(shí)現(xiàn)的有序。
不過,下面的實(shí)現(xiàn)是有問題的,這個cache的key:value鍵值對中,value只能是不可變類型。因?yàn)?,如果value是可變類型,那對于同一個key,所有調(diào)用get(key)方法返回的value都是指向同一個可變對象的,當(dāng)修改其中一個value時,那所有的value都會被修改了,即使你沒有調(diào)用set()方法也會這樣。這是我們不希望看到的。解決方法我想到了兩種,一是可變對象序列化后再存儲,即將可變對象轉(zhuǎn)為不可變對象;二是仍存儲可變對象,但get()時,返回一個深拷貝,這樣每個get()調(diào)用返回的對象就不會相互影響了。推薦第一種方法。另外,對于key,推薦使用str/unicode類型。
當(dāng)并發(fā)時,還會存在一個問題,因?yàn)檫@涉及到對公共資源的寫操作,所以必須要對set()加鎖。其實(shí),在并發(fā)情況下,所有對公共資源的寫操作都要加鎖。如果不存在并發(fā)的情況,只有單線程,那可以不加鎖。
方法一:用OrderedDict實(shí)現(xiàn)(推薦)
from collections import OrderedDict
class LRUCache(OrderedDict):
'''不能存儲可變類型對象,不能并發(fā)訪問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實(shí)現(xiàn)(不推薦)
class LRUCache(object):
'''不能存儲可變類型對象,不能并發(fā)訪問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中緩存lru_cache的基本介紹和講解
- Python 如何手動編寫一個自己的LRU緩存裝飾器的方法實(shí)現(xiàn)
- python自帶緩存lru_cache用法及擴(kuò)展的使用
- Python 的lru_cache裝飾器使用簡介
- Python中l(wèi)ru_cache的使用和實(shí)現(xiàn)詳解
- 工程師必須了解的LRU緩存淘汰算法以及python實(shí)現(xiàn)過程
- python實(shí)現(xiàn)LRU熱點(diǎn)緩存及原理
- LRUCache的實(shí)現(xiàn)原理及利用python實(shí)現(xiàn)的方法
- Python實(shí)現(xiàn)的一個簡單LRU cache
- Python實(shí)現(xiàn)LRU算法
相關(guān)文章
解決Python import docx出錯DLL load failed的問題
今天小編就為大家分享一篇解決Python import docx出錯DLL load failed的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-02-02Python中LSTM回歸神經(jīng)網(wǎng)絡(luò)時間序列預(yù)測詳情
這篇文章主要介紹了Python中LSTM回歸神經(jīng)網(wǎng)絡(luò)時間序列預(yù)測詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-07-07Python PyQt4實(shí)現(xiàn)QQ抽屜效果
這篇文章主要為大家詳細(xì)介紹了Python PyQt4實(shí)現(xiàn)QQ抽屜效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04Python一行代碼實(shí)現(xiàn)生成和讀取二維碼
二維碼被稱為快速響應(yīng)碼,可能看起來很簡單,但它們能夠存儲大量數(shù)據(jù)。無論掃描二維碼時包含多少數(shù)據(jù),用戶都可以立即訪問信息。本文將用一行Python代碼實(shí)現(xiàn)二維碼的讀取與生成,需要的可以參考一下2022-02-02