Python?cachetools實(shí)現(xiàn)緩存過(guò)期策略
cachetools 是一個(gè)功能強(qiáng)大的 Python 庫(kù),用于實(shí)現(xiàn)多種緩存策略(如 LRU、TTL、LFU 等),幫助開(kāi)發(fā)者優(yōu)化程序性能。無(wú)論是頻繁調(diào)用高開(kāi)銷(xiāo)函數(shù),還是減少重復(fù)計(jì)算,cachetools 都能顯著提升效率。以下是詳細(xì)的基礎(chǔ)知識(shí)點(diǎn)講解及豐富的代碼案例。
安裝與配置
在開(kāi)始使用 cachetools 之前,需要通過(guò) pip 安裝:
pip install cachetools
安裝完成后,即可直接使用,無(wú)需復(fù)雜配置。
核心概念與用法
1. 緩存類(lèi)型介紹
- LRUCache(Least Recently Used) :最近最少使用緩存,會(huì)優(yōu)先移除最久未被訪(fǎng)問(wèn)的項(xiàng)目。
- TTLCache(Time-To-Live) :設(shè)置緩存項(xiàng)的存活時(shí)間,過(guò)期后自動(dòng)刪除。
- LFUCache(Least Frequently Used) :最少頻繁使用緩存,會(huì)移除訪(fǎng)問(wèn)頻率最低的項(xiàng)目。
2. 創(chuàng)建緩存對(duì)象
以下是創(chuàng)建不同類(lèi)型緩存的示例:
from cachetools import LRUCache, TTLCache, LFUCache # 創(chuàng)建 LRU 緩存,最大容量為 5 lru_cache = LRUCache(maxsize=5) # 創(chuàng)建 TTL 緩存,最大容量為 10,每項(xiàng)存活時(shí)間為 60 秒 ttl_cache = TTLCache(maxsize=10, ttl=60) # 創(chuàng)建 LFU 緩存,最大容量為 3 lfu_cache = LFUCache(maxsize=3)
3. 函數(shù)結(jié)果緩存
通過(guò)裝飾器將函數(shù)結(jié)果緩存起來(lái),避免重復(fù)計(jì)算:
from cachetools import cached, TTLCache
# 創(chuàng)建一個(gè) TTL 緩存
cache = TTLCache(maxsize=3, ttl=10)
@cached(cache)
def expensive_computation(x):
print(f"Computing {x}...")
return x * x
# 第一次調(diào)用會(huì)計(jì)算并緩存
print(expensive_computation(4)) # 輸出: 16
# 第二次調(diào)用直接從緩存獲取
print(expensive_computation(4)) # 輸出: 16(無(wú)計(jì)算)
注意:當(dāng)緩存項(xiàng)過(guò)期或超出容量時(shí),將觸發(fā)重新計(jì)算。
4. LRU 緩存示例
LRU 緩存會(huì)自動(dòng)移除最近最少使用的項(xiàng)目:
from cachetools import LRUCache
cache = LRUCache(maxsize=2)
# 添加數(shù)據(jù)到緩存中
cache['a'] = 1
cache['b'] = 2
print(cache) # 輸出: {'a': 1, 'b': 2}
# 添加新數(shù)據(jù),超出容量時(shí)移除最少使用的數(shù)據(jù)
cache['c'] = 3
print(cache) # 輸出: {'b': 2, 'c': 3}
5. 自定義鍵生成函數(shù)
可以通過(guò)自定義鍵函數(shù)處理復(fù)雜參數(shù),如忽略某些參數(shù)或處理不可哈希對(duì)象:
from cachetools import cached, LRUCache, keys
def custom_key(*args, **kwargs):
return keys.hashkey(*args) + tuple(sorted(kwargs.items()))
cache = LRUCache(maxsize=128)
@cached(cache, key=custom_key)
def fetch_data(x, y, z=None):
print(f"Fetching data for {x}, {y}, {z}")
return x + y + (z or 0)
print(fetch_data(1, 2, z=3)) # 實(shí)際調(diào)用
print(fetch_data(1, 2, z=3)) # 從緩存中獲取
6. 完整案例:API 數(shù)據(jù)緩存
以下示例展示如何利用 TTLCache 緩存 API 請(qǐng)求結(jié)果,避免重復(fù)網(wǎng)絡(luò)請(qǐng)求:
import requests
from cachetools import cached, TTLCache
# 創(chuàng)建一個(gè) TTL 緩存,最大容量為 5,每項(xiàng)生存時(shí)間為 30 秒
api_cache = TTLCache(maxsize=5, ttl=30)
@cached(api_cache)
def fetch_api_data(url):
print(f"Fetching data from {url}")
response = requests.get(url)
return response.json() if response.status_code == 200 else None
url = "https://jsonplaceholder.typicode.com/todos/1"
print(fetch_api_data(url)) # 實(shí)際請(qǐng)求 API
print(fetch_api_data(url)) # 從緩存獲取結(jié)果
性能測(cè)試與優(yōu)化效果
以下是 cachetools 在實(shí)際應(yīng)用中的性能提升效果:
| 場(chǎng)景 | 無(wú)緩存耗時(shí) | 使用 cachetools 耗時(shí) | 提升比例 |
|---|---|---|---|
| Fibonacci 數(shù)列計(jì)算 | ~2 秒 | ~0.01 秒 | ~200 倍 |
| API 數(shù)據(jù)請(qǐng)求(10 次相同) | ~10 秒 | ~1 秒 | ~10 倍 |
通過(guò)減少重復(fù)計(jì)算和網(wǎng)絡(luò)請(qǐng)求,cachetools 可顯著節(jié)省資源。
Python cachetools 實(shí)現(xiàn)緩存過(guò)期策略
實(shí)現(xiàn)代碼
from cachetools import TTLCache
import time
class CallbackTTLCache(TTLCache):
"""支持過(guò)期回調(diào)的 TTL 緩存"""
def __init__(self, maxsize, ttl, callback=None):
super().__init__(maxsize, ttl)
self.callback = callback # 過(guò)期回調(diào)函數(shù)
self._pending_callbacks = {} # 跟蹤需要回調(diào)的項(xiàng)目
def __setitem__(self, key, value, **kwargs):
super().__setitem__(key, value, **kwargs)
if self.callback:
self._pending_callbacks[key] = value
def __getitem__(self, key):
try:
return super().__getitem__(key)
except KeyError:
if key in self._pending_callbacks:
value = self._pending_callbacks.pop(key)
if self.callback:
self.callback(key, value)
raise
def popitem(self):
"""淘汰條目時(shí)觸發(fā)回調(diào)"""
try:
key, value = super().popitem()
if key in self._pending_callbacks:
self._pending_callbacks.pop(key)
if self.callback:
self.callback(key, value)
return key, value
except KeyError:
print("緩存已經(jīng)為空")
return None, None
def clear_expired(self):
"""清理所有過(guò)期的項(xiàng)目并觸發(fā)回調(diào)"""
current_time = time.time()
expired_keys = []
# 檢查所有鍵是否過(guò)期
for key in list(self._pending_callbacks):
try:
# 嘗試獲取值,如果過(guò)期會(huì)拋出KeyError
_ = self[key]
except KeyError:
# 如果拋出KeyError,說(shuō)明已過(guò)期
expired_keys.append(key)
# 觸發(fā)過(guò)期回調(diào)
for key in expired_keys:
if key in self._pending_callbacks:
value = self._pending_callbacks.pop(key)
if self.callback:
self.callback(key, value)
def clear(self):
"""清空緩存并觸發(fā)所有回調(diào)"""
for key, value in list(self._pending_callbacks.items()):
if self.callback:
self.callback(key, value)
self._pending_callbacks.clear()
super().clear()
# 示例:定義過(guò)期回調(diào)函數(shù)
def on_expired(key, value):
print(f"?? 緩存過(guò)期!Key: {key}, Value: {value} 已清除")
# 初始化緩存(最大100條,有效期5秒,綁定回調(diào))
cache = CallbackTTLCache(maxsize=100, ttl=5, callback=on_expired)
# 測(cè)試用例
def run_tests():
print("=== 開(kāi)始測(cè)試 ===")
# 測(cè)試1:添加并立即訪(fǎng)問(wèn)緩存
print("\n測(cè)試1:添加并立即訪(fǎng)問(wèn)緩存")
cache["user_101"] = "Alice"
cache["user_102"] = "Bob"
print("初始緩存內(nèi)容:", dict(cache.items()))
# 測(cè)試2:等待部分時(shí)間后訪(fǎng)問(wèn)
print("\n測(cè)試2:等待3秒后訪(fǎng)問(wèn)緩存")
time.sleep(3)
print("訪(fǎng)問(wèn)user_101:", cache.get("user_101", "已過(guò)期"))
print("3秒后緩存內(nèi)容:", dict(cache.items()))
# 測(cè)試3:等待過(guò)期并嘗試訪(fǎng)問(wèn)
print("\n測(cè)試3:等待剩余時(shí)間直到過(guò)期")
time.sleep(3) # 再等3秒,總共6秒,確保過(guò)期
print("嘗試訪(fǎng)問(wèn)user_101:", cache.get("user_101", "已過(guò)期"))
print("6秒后(過(guò)期)緩存內(nèi)容:", dict(cache.items()))
# 測(cè)試4:手動(dòng)清理過(guò)期內(nèi)容
print("\n測(cè)試4:清理過(guò)期內(nèi)容")
cache.clear_expired()
print("清理后緩存內(nèi)容:", dict(cache.items()))
# 測(cè)試5:嘗試對(duì)空緩存調(diào)用popitem
print("\n測(cè)試5:對(duì)空緩存調(diào)用popitem")
key, value = cache.popitem()
print(f"popitem結(jié)果 - Key: {key}, Value: {value}")
print("\n=== 測(cè)試完成 ===")
if __name__ == "__main__":
run_tests()import time
from functools import wraps
def timed_cache(ttl=60, callback=None):
"""支持過(guò)期時(shí)間和回調(diào)的緩存裝飾器"""
cache = {}
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
key = (args, frozenset(kwargs.items()))
current_time = time.time()
# 檢查緩存是否命中且未過(guò)期
if key in cache:
result, expire_time = cache[key]
if current_time < expire_time:
return result
elif callback: # 觸發(fā)過(guò)期回調(diào)
callback(key, result)
# 重新計(jì)算并緩存結(jié)果
result = func(*args, **kwargs)
cache[key] = (result, current_time + ttl)
return result
return wrapper
return decorator
# 定義回調(diào)函數(shù)
def expire_callback(key, value):
print(f"?? 回調(diào)通知: 參數(shù) {key} 的值 {value} 已過(guò)期")
# 應(yīng)用裝飾器
@timed_cache(ttl=3, callback=expire_callback)
def heavy_compute(x):
print(f"計(jì)算中... 參數(shù)={x}")
return x ** 2
# 測(cè)試
print(heavy_compute(4)) # 首次計(jì)算,輸出: 計(jì)算中... 16
print(heavy_compute(4)) # 命中緩存,無(wú)輸出 → 16
time.sleep(4)
print(heavy_compute(4)) # 過(guò)期后重新計(jì)算,觸發(fā)回調(diào) → 計(jì)算中... 16總結(jié)與建議
cachetools提供了多種靈活的緩存策略(如 LRU、TTL、LFU 等),適用于不同場(chǎng)景。- 使用簡(jiǎn)單,可通過(guò)裝飾器快速實(shí)現(xiàn)函數(shù)結(jié)果緩存。
- 在實(shí)際開(kāi)發(fā)中,應(yīng)根據(jù)需求選擇合適的緩存類(lèi)型和參數(shù)配置。
推薦在 Web 開(kāi)發(fā)、機(jī)器學(xué)習(xí)、大數(shù)據(jù)處理等需要高效數(shù)據(jù)訪(fǎng)問(wèn)的場(chǎng)景中使用 cachetools。
到此這篇關(guān)于Python cachetools實(shí)現(xiàn)緩存過(guò)期策略的文章就介紹到這了,更多相關(guān)Python cachetools緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Python將數(shù)組的元素導(dǎo)出到變量中(unpacking)
最近工作中遇到一個(gè)問(wèn)題,需要利用Python將數(shù)組(list)或元組(tuple)中的元素導(dǎo)出到N個(gè)變量中,現(xiàn)在將我實(shí)現(xiàn)的方法分享給大家,有需要的朋友們可以參考借鑒,下面來(lái)一起看看吧。2016-10-10
python3安裝OCR識(shí)別庫(kù)tesserocr過(guò)程圖解
這篇文章主要介紹了python3安裝OCR識(shí)別庫(kù)tesserocr過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
jupyter notebook出現(xiàn)In[*]的問(wèn)題及解決
這篇文章主要介紹了jupyter notebook出現(xiàn)In[*]的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
Python使用Chardet庫(kù)檢測(cè)字符編碼的操作詳解
在處理文本數(shù)據(jù)時(shí),字符編碼問(wèn)題是一個(gè)常見(jiàn)的挑戰(zhàn),如果編碼不正確,可能會(huì)導(dǎo)致亂碼問(wèn)題,而 Chardet 是 Python 中非常實(shí)用的一個(gè)庫(kù),可以幫助我們快速檢測(cè)文件或字符串的編碼格式,本文給大家詳細(xì)介紹了Python Chardet 庫(kù)用法,需要的朋友可以參考下2025-01-01
python write無(wú)法寫(xiě)入文件的解決方法
今天小編就為大家分享一篇python write無(wú)法寫(xiě)入文件的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01
python wordcloud庫(kù)實(shí)例講解使用方法
這篇文章主要介紹了python wordcloud庫(kù)實(shí)例,詞云通過(guò)以詞語(yǔ)為基本單位,更加直觀和藝術(shù)地展示文本。wordcloud是優(yōu)秀的詞云展示的python第三方庫(kù)2022-12-12
Django之第三方平臺(tái)QQ授權(quán)登錄的實(shí)現(xiàn)
本文主要介紹了Django之第三方平臺(tái)QQ授權(quán)登錄的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
pytorch中如何使用DataLoader對(duì)數(shù)據(jù)集進(jìn)行批處理的方法
這篇文章主要介紹了pytorch中如何使用DataLoader對(duì)數(shù)據(jù)集進(jìn)行批處理的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
python中如何實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用
這篇文章主要介紹了python中如何實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,幫助大家更好的理解和學(xué)習(xí)使用python,感興趣的朋友可以了解下2021-03-03

