用functools.lru_cache實現Python的Memoization


現在你已經看到了如何自己實現一個memoization函數,我會告訴你,你可以使用Python的functools.lru_cache裝飾器來獲得相同的結果,以增加方便性。

我最喜歡Python的原因之一就是它的語法的簡潔和美麗與它的哲學的美麗和簡單性並行不悖。Python被稱作“內置電池(batteries included)”,這意味着Python捆綁了大量常用的庫和模塊,這些只需要一個import聲明!

我發現functools.lru_cache是一個很好的例子。lru_cache裝飾器是Python標准庫實現的易於使用的記憶功能。一旦你認識到什么時候使用lru_cache,你只需幾行代碼就可以快速加快你的應用程序。

我們再來看看我們的斐波那契數列示例。這一次,我會告訴你如何使用functools.lru_cache裝飾器添加記憶:

用functools.lru_cache實現Python的Memoization

請注意我給lru_cache傳遞的maxsize參數是同時來限制存儲在緩存中的項目數量。

我再一次使用該timeit模塊來運行一個簡單的基准測試,以便了解這種優化對性能的影響:

用functools.lru_cache實現Python的Memoization

您可能想知道,為什么我們這次能夠以更快的速度獲得第一次運行的結果。第一次運行緩存不應該是 “凍結”的嗎?

不同的是,在這個例子中,我在函數定義的時候使用了@lru_cache裝飾器。這意味着這次遞歸調用fibonacci也在緩存中查找。

通過@lru_cache裝飾器裝飾fibonacci函數,我基本上把它變成了一個動態編程解決方案,每個子問題只需要存儲一次子問題解決方案,並在下次嘗試解決相同問題時從緩存中查找結果。

這只是一個例子——但我相信你開始能夠看到使用memoization裝飾器的美麗和強大,並且開始意識到實現一個動態算法能夠帶來多大的好處。

 

為什么你應該喜歡 functools.lru_cache

一般來說,由functools.lru_cache實現的Python的memoization比我們的專用memoize函數更全面,就像你在CPython源代碼中看到的一樣。

例如,它提供了一個方便的功能,允許您使用cache_info方法檢索緩存統計信息:

用functools.lru_cache實現Python的Memoization

再一次,正如你在CacheInfo輸出中看到的那樣,Python的lru_cache記住了遞歸調用fibonacci。當我們查看memoized函數的緩存信息時,您會發現為什么它在第一次運行時比我們的版本更快——緩存命中了34次。

正如我之前所暗示的,functools.lru_cache還允許您使用maxsize參數限制緩存結果的數量。通過設置maxsize=None你可以強制緩存是無界的,我通常會反對這樣做。

還有一個typed布爾參數可以設置為True告訴緩存,不同類型的函數參數應該分開緩存。例如,fibonacci(35)和fibonacci(35.0)將被視為產生截然不同結果的不同調用。

另一個有用的功能是可以隨時使用cache_clear方法重置結果緩存:

用functools.lru_cache實現Python的Memoization

如果您想了解更多關於使用lru_cache裝飾器的復雜信息,我建議您參考Python標准庫文檔。

總之,你永遠不需要推出自己的記憶功能。Python的內置方法lru_cache是易於使用的,更全面和經過測試的。

 

緩存注意事項——什么是可以被記憶的?

理想情況下,您將要記憶確定性的函數。

用functools.lru_cache實現Python的Memoization

這deterministic_adder是一個確定性函數,因為它總是會為相同的一對參數返回相同的結果。例如,如果您將2和3傳入該函數,它將始終返回5。

將此行為與以下非確定性函數進行比較:

用functools.lru_cache實現Python的Memoization

這個函數是不確定的,因為它對於一個給定的輸入的輸出會根據星期幾而變化:如果你在星期一運行這個函數,緩存將在一周中的任何一天返回陳舊的數據。

一般來說,我發現任何更新記錄或返回隨時間變化的信息的函數對於記憶都是不好的選擇。

或者,正如Phil Karlton所說:

計算機科學只有兩件難事:緩存失效和命名事物。

——Phil Karlton

 

Python中的記憶:快速總結

在這篇Python教程中,您看到了memoization如何通過基於提供給它的參數緩存輸出來優化函數。

一旦你記憶一個函數,它將只為你調用的每一組參數計算一次輸出。第一次調用之后的每次調用都將快速從緩存中檢索出來。

您看到了如何從頭開始編寫自己的memoization裝飾器,以及為什么在生產代碼中您可能想要用Python內置的lru_cache:

記憶是一種軟件優化技術,它根據參數存儲返回函數調用的結果。

如果你的代碼符合某個標准,memoization可以是一個很好的方法來加快你的應用程序。

您可以從Python標准庫中導入一個全面的memoization函數,functools模塊中的lru_cache。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM