一文帶你深入理解Python的`functools.lru_cache`裝飾器
一、什么是 functools.lru_cache?
functools.lru_cache
是 Python 標(biāo)準(zhǔn)庫(kù)中 functools
模塊的一部分。lru_cache
裝飾器可以用來(lái)為一個(gè)函數(shù)添加一個(gè)緩存系統(tǒng)。這個(gè)緩存系統(tǒng)會(huì)存儲(chǔ)函數(shù)的輸入和對(duì)應(yīng)的輸出。如果函數(shù)被調(diào)用,并且給出了已經(jīng)緩存過(guò)的輸入,那么函數(shù)就不會(huì)重新計(jì)算,而是直接從緩存中獲取對(duì)應(yīng)的輸出。
LRU 是 “Least Recently Used” 的縮寫(xiě),意思是 “最近最少使用”。LRU 緩存就是一種緩存淘汰算法,當(dāng)緩存達(dá)到預(yù)設(shè)的容量上限時(shí),會(huì)優(yōu)先淘汰最近最少使用的數(shù)據(jù)。
from functools import lru_cache @lru_cache(maxsize=None) def fib(n): if n < 2: return n return fib(n - 1) + fib(n - 2) print(fib(10)) # 輸出:55
在上面的例子中,我們定義了一個(gè)求斐波那契數(shù)列的函數(shù),并且使用 @lru_cache(maxsize=None)
裝飾器對(duì)其進(jìn)行了裝飾。然后我們調(diào)用 fib(10)
,得到結(jié)果 55。實(shí)際上,由于使用了緩存,fib
函數(shù)在求解過(guò)程中,對(duì)于同樣的參數(shù)只進(jìn)行了一次計(jì)算。
二、如何使用 functools.lru_cache?
要使用 functools.lru_cache
裝飾器,你只需要在你的函數(shù)定義之前添加 @functools.lru_cache
行。這會(huì)讓 lru_cache
裝飾器知道你希望為這個(gè)函數(shù)添加一個(gè)緩存系統(tǒng)。
lru_cache
裝飾器有兩個(gè)可選參數(shù):
maxsize
:這個(gè)參數(shù)用來(lái)設(shè)置緩存的大小。如果你設(shè)置了這個(gè)參數(shù),緩存的大小就會(huì)被限制在這個(gè)值之內(nèi)。如果你不設(shè)置這個(gè)參數(shù),或者將其設(shè)置為None
,那么緩存的大小就沒(méi)有上限。typed
:如果你將這個(gè)參數(shù)設(shè)置為True
,那么lru_cache
就會(huì)根據(jù)輸入?yún)?shù)的類(lèi)型分別進(jìn)行緩存。也就是說(shuō),1
和1.0
盡管在 Python 中是相等的,但它們會(huì)被當(dāng)成兩個(gè)不同的輸入進(jìn)行緩存。默認(rèn)情況下,typed
參數(shù)是False
。
from functools import lru_cache @lru_cache(maxsize=128, typed=False) def add(x, y): print(f"Calculating: {x} + {y}") return x + y print(add(1, 2)) # 輸出:Calculating: 1 + 2 \n 3 print(add(1, 2)) # 輸出:3 print(add(1.0, 2.0)) # 輸出:Calculating: 1.0 + 2.0 \n 3.0 print(add(1.0, 2.0)) # 輸出:3.0
在上面的代碼中,我們定義了一個(gè)加法函數(shù) add
,并使用 lru_cache
裝飾器對(duì)其進(jìn)行裝飾。我們可以看到,當(dāng)我們第二次調(diào)用 add(1, 2)
和 add(1.0, 2.0)
時(shí),add
函數(shù)并沒(méi)有重新進(jìn)行計(jì)算,而是直接從緩存中獲取了結(jié)果。
三、functools.lru_cache 的用途
functools.lru_cache
可以用于優(yōu)化那些具有重復(fù)計(jì)算的遞歸函數(shù),或者計(jì)算成本較高的函數(shù)。通過(guò)保存已經(jīng)計(jì)算過(guò)的值,functools.lru_cache
能夠避免重復(fù)的計(jì)算,從而提高程序的運(yùn)行效率。
例如,求解斐波那契數(shù)列就是一個(gè)典型的使用場(chǎng)景。在沒(méi)有優(yōu)化的情況下,求解斐波那契數(shù)列的時(shí)間復(fù)雜度是指數(shù)級(jí)別的。但是,如果我們使用 functools.lru_cache
對(duì)其進(jìn)行優(yōu)化,那么我們就可以將其時(shí)間復(fù)雜度降低到線性級(jí)別。
此外,functools.lru_cache
還可以用于緩存那些對(duì)數(shù)據(jù)庫(kù)或者文件系統(tǒng)的重復(fù)查詢,從而提高程序的性能。
需要注意的是,functools.lru_cache
并不適合所有的場(chǎng)景。因?yàn)?functools.lru_cache
是通過(guò)空間換取時(shí)間的方式來(lái)提高程序的性能的,所以,如果你的程序運(yùn)行在內(nèi)存有限的環(huán)境中,或者你的函數(shù)有大量的不同輸入,那么使用 functools.lru_cache
可能會(huì)導(dǎo)致內(nèi)存消耗過(guò)大。此外,如果你的函數(shù)有副作用,或者依賴于外部狀態(tài),那么 functools.lru_cache
也可能無(wú)法正確地工作。在這些情況下,你可能需要尋找其他的優(yōu)化策略。
總的來(lái)說(shuō),functools.lru_cache
是一個(gè)非常強(qiáng)大的工具,它能夠幫助我們優(yōu)化代碼,提高程序的性能。當(dāng)你在編寫(xiě)一個(gè)計(jì)算密集型或者需要大量重復(fù)計(jì)算的函數(shù)時(shí),不妨考慮使用 functools.lru_cache
對(duì)其進(jìn)行優(yōu)化。
四、深入理解 functools.lru_cache
當(dāng)我們將 functools.lru_cache
應(yīng)用到函數(shù)上時(shí),每次調(diào)用函數(shù),它都會(huì)檢查其參數(shù)是否已經(jīng)在緩存中。如果在緩存中,它將返回緩存的結(jié)果,而不需要重新計(jì)算。如果沒(méi)有在緩存中,那么函數(shù)將被調(diào)用并且結(jié)果將被添加到緩存中。當(dāng)緩存滿了,最少使用的條目將被拋棄。
以下是一個(gè)理解 functools.lru_cache
工作方式的例子:
from functools import lru_cache @lru_cache(maxsize=3) def foo(n): print(f"Running foo({n})") return n print(foo(1)) # 輸出:Running foo(1) \n 1 print(foo(2)) # 輸出:Running foo(2) \n 2 print(foo(3)) # 輸出:Running foo(3) \n 3 print(foo(1)) # 輸出:1 print(foo(2)) # 輸出:2 print(foo(3)) # 輸出:3 print(foo(4)) # 輸出:Running foo(4) \n 4 print(foo(1)) # 輸出:Running foo(1) \n 1
在這個(gè)例子中,我們?cè)O(shè)定 maxsize=3
,也就是只緩存最近的三個(gè)結(jié)果。當(dāng)我們連續(xù)調(diào)用 foo(1)
,foo(2)
,foo(3)
時(shí),這三個(gè)結(jié)果都被緩存了下來(lái)。再次調(diào)用這三個(gè)函數(shù)時(shí),由于結(jié)果已經(jīng)在緩存中,函數(shù)并沒(méi)有被重新執(zhí)行。但是當(dāng)我們調(diào)用 foo(4)
時(shí),由于緩存已滿,所以最早被緩存的 foo(1)
的結(jié)果被移除了。再次調(diào)用 foo(1)
時(shí),函數(shù)需要被重新執(zhí)行。
這個(gè)例子說(shuō)明了 functools.lru_cache
的 LRU 特性:當(dāng)緩存達(dá)到上限時(shí),最近最少使用的緩存會(huì)被移除。
五、清理和查看緩存
functools.lru_cache
還提供了兩個(gè)方法用于清理和查看緩存:cache_clear
和 cache_info
。
cache_clear
方法可以清空所有的緩存。例如,在上面的 foo
函數(shù)中,我們可以通過(guò) foo.cache_clear()
來(lái)清空所有的緩存。
cache_info
方法返回一個(gè)命名元組,描述了緩存的狀態(tài)。它包含以下幾個(gè)字段:hits
、misses
、maxsize
和 currsize
。其中,hits
和 misses
分別表示緩存命中和未命中的次數(shù),maxsize
表示緩存的最大容量,currsize
表示當(dāng)前緩存的使用量。
from functools import lru_cache @lru_cache(maxsize=3) def foo(n): print(f"Running foo({n})") return n foo(1) foo(2) foo(3) foo(4) print(foo.cache_info()) # 輸出:CacheInfo(hits=0, misses=4, maxsize=3, currsize=3) foo(4) print(foo.cache_info()) # 輸出:CacheInfo(hits=1, misses=4, maxsize=3, currsize=3) foo.cache_clear() print(foo.cache_info()) # 輸出:CacheInfo(hits=0, misses=0, maxsize=3, currsize=0)
在這個(gè)例子中,我們首先調(diào)用了 foo(1)
,foo(2)
,foo(3)
和 foo(4)
。此時(shí),由于 foo(1)
的緩存已經(jīng)被淘汰,緩存中僅保留了 foo(2)
,foo(3)
和 foo(4)
的結(jié)果。調(diào)用 foo.cache_info()
,我們可以看到緩存未命中的次數(shù)為 4,當(dāng)前緩存的使用量為 3。
然后我們?cè)俅握{(diào)用 foo(4)
,由于這個(gè)結(jié)果已經(jīng)在緩存中,所以這次是緩存命中,調(diào)用 foo.cache_info()
,我們可以看到緩存命中的次數(shù)變成了 1。
最后,我們調(diào)用 foo.cache_clear()
清空了所有的緩存,再次調(diào)用 foo.cache_info()
,我們可以看到當(dāng)前緩存的使用量變成了 0。
以上,我們介紹了 functools.lru_cache
裝飾器的使用方法和原理,包括如何使用 lru_cache
對(duì)函數(shù)進(jìn)行優(yōu)化,以及如何清理和查看緩存。希望這篇文章能夠幫助你更好地理解和使用 functools.lru_cache
。
到此這篇關(guān)于一文帶你深入理解Python的`functools.lru_cache`裝飾器的文章就介紹到這了,更多相關(guān)Python `functools.lru_cache`裝飾器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python寫(xiě)入數(shù)據(jù)到csv或xlsx文件的3種方法
這篇文章主要為大家詳細(xì)介紹了python寫(xiě)入數(shù)據(jù)到csv或xlsx文件的3種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08python實(shí)現(xiàn)靜態(tài)服務(wù)器
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)靜態(tài)服務(wù)器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09python之matplotlib學(xué)習(xí)繪制動(dòng)態(tài)更新圖實(shí)例代碼
這篇文章主要介紹了python之matplotlib學(xué)習(xí)繪制動(dòng)態(tài)更新圖實(shí)例代碼,文中涉及具體實(shí)現(xiàn)代碼,演示效果及運(yùn)行時(shí)出現(xiàn)的問(wèn)題分析等相關(guān)內(nèi)容,小編覺(jué)得還是挺不錯(cuò)的,這里分享給大家,需要的朋友可以參考下2018-01-01Python中Numpy和Matplotlib的基本使用指南
numpy庫(kù)處理的最基礎(chǔ)數(shù)據(jù)類(lèi)型是由同種元素構(gòu)成的多維數(shù)組(ndarray),而matplotlib 是提供數(shù)據(jù)繪圖功能的第三方庫(kù),其pyplot子庫(kù)主要用于實(shí)現(xiàn)各種數(shù)據(jù)展示圖形的繪制,這篇文章主要給大家介紹了關(guān)于Python中Numpy和Matplotlib的基本使用指南,需要的朋友可以參考下2021-11-11python實(shí)現(xiàn)商品進(jìn)銷(xiāo)存管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)商品進(jìn)銷(xiāo)存管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05Python random模塊制作簡(jiǎn)易的四位數(shù)驗(yàn)證碼
這篇文章主要介紹了Python random模塊制作簡(jiǎn)易的四位數(shù)驗(yàn)證碼,文中給大家提到了python中random模塊的相關(guān)知識(shí),需要的朋友可以參考下2020-02-02Python使用lambda表達(dá)式對(duì)字典排序操作示例
這篇文章主要介紹了Python使用lambda表達(dá)式對(duì)字典排序操作,結(jié)合實(shí)例形式分析了lambda表達(dá)式實(shí)現(xiàn)字典按鍵排序、按值排序、多條件排序相關(guān)操作技巧,需要的朋友可以參考下2019-07-07詳解Python開(kāi)發(fā)中如何使用Hook技巧
這篇文章主要介紹了詳解Python開(kāi)發(fā)中如何使用Hook技巧,詳細(xì)的介紹了Python Hook的用法和示例,有興趣的可以了解一下2017-11-11