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)先移除最久未被訪問(wèn)的項(xiàng)目。
- TTLCache(Time-To-Live) :設(shè)置緩存項(xiàng)的存活時(shí)間,過(guò)期后自動(dòng)刪除。
- LFUCache(Least Frequently Used) :最少頻繁使用緩存,會(huì)移除訪問(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:添加并立即訪問(wèn)緩存
print("\n測(cè)試1:添加并立即訪問(wèn)緩存")
cache["user_101"] = "Alice"
cache["user_102"] = "Bob"
print("初始緩存內(nèi)容:", dict(cache.items()))
# 測(cè)試2:等待部分時(shí)間后訪問(wèn)
print("\n測(cè)試2:等待3秒后訪問(wèn)緩存")
time.sleep(3)
print("訪問(wèn)user_101:", cache.get("user_101", "已過(guò)期"))
print("3秒后緩存內(nèi)容:", dict(cache.items()))
# 測(cè)試3:等待過(guò)期并嘗試訪問(wèn)
print("\n測(cè)試3:等待剩余時(shí)間直到過(guò)期")
time.sleep(3) # 再等3秒,總共6秒,確保過(guò)期
print("嘗試訪問(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ù)訪問(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之DataFrame實(shí)現(xiàn)excel合并單元格
這篇文章主要為大家詳細(xì)介紹了python之DataFrame實(shí)現(xiàn)excel合并單元格,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04
python中不同數(shù)據(jù)對(duì)象的空值校驗(yàn)總結(jié)
在Python中,我們可以使用不同的方式來(lái)校驗(yàn)數(shù)值的空值、字符串的空值以及對(duì)象的空值,本文為大家整理了一些常見(jiàn)的方法,希望對(duì)大家有所幫助2024-01-01
如何將python項(xiàng)目部署在一臺(tái)服務(wù)器上
服務(wù)器less技術(shù)是一種無(wú)需管理服務(wù)器即可運(yùn)行應(yīng)用程序的方法,最流行的服務(wù)器less平臺(tái)是AWS Lambda,這篇文章主要介紹了如何將python項(xiàng)目部署在一臺(tái)服務(wù)器上,需要的朋友可以參考下2023-10-10
深度解析Python魔法函數(shù)中__getattr__與__getattribute__的核心奧義
Python作為一門(mén)極具靈活性的動(dòng)態(tài)類(lèi)型語(yǔ)言,其驚艷的動(dòng)態(tài)特性并非憑空而來(lái),魔法函數(shù)正是支撐這一特性的底層靈魂,下面小編就和大家詳細(xì)介紹一下_getattr__與__getattribute__的調(diào)用機(jī)制,核心區(qū)別與實(shí)戰(zhàn)應(yīng)用吧2026-03-03
利用Python批量循環(huán)讀取Excel的技巧分享
這篇文章主要為大家詳細(xì)介紹了何用Python批量循環(huán)讀取Excel,文中的示例代碼講解詳細(xì),對(duì)我們的學(xué)習(xí)或工作有一定的幫助,感興趣的可以了解一下2023-07-07

