10分鐘掌握Python快取

程序员世杰發表於2024-06-26

全文速覽

  • python的不同快取元件的使用場景和使用樣例
  • cachetools的使用

專案背景

程式碼檢查專案,需要儲存每一步檢查的中間結果,最終把結果彙總並寫入檔案中

在中間結果的儲存中

  • 可以使用context進行上下文的傳遞,但是整體對程式碼改動比較大,違背了開閉原則
  • 也可以利用快取儲存,處理完成之後再統一讀快取並寫入檔案

在權衡了不同方案後,我決定採用快取來儲存中間結果。接下來,我將探討 Python 中可用快取元件。

python快取分類

決定選擇快取,那麼python中都有哪些型別的快取呢?

1. 使用記憶體快取(如 functools.lru_cache

這是最簡單的一種快取方法,適用於小規模的資料快取。使用 functools.lru_cache 可以對函式結果進行快取。

from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_function(param1, param2):
    # 進行一些耗時的操作
    return result

2. 使用本地檔案快取(如 diskcache

如果快取的資料較大,或者需要跨程序共享快取,可以使用檔案系統快取庫,例如 diskcache

import diskcache as dc

cache = dc.Cache('/tmp/mycache')

@cache.memoize(expire=3600)
def expensive_function(param1, param2):
    # 進行一些耗時的操作
    return result

3. 使用分散式快取(如 Redis)

對於需要跨多個應用例項共享快取的資料,可以使用 Redis 這樣的分散式快取系統。

import redis
import pickle

r = redis.StrictRedis(host='localhost', port=6379, db=0)

def expensive_function(param1, param2):
    key = f"{param1}_{param2}"
    cached_result = r.get(key)
    if cached_result:
        return pickle.loads(cached_result)
    
    result = # 進行一些耗時的操作
    r.set(key, pickle.dumps(result), ex=3600)  # 設定快取過期時間為1小時
    return result

總結

如果只是簡單的小規模快取,lru_cache 足夠;如果需要持久化或分散式快取,可以考慮使用 diskcache 或 Redis;如果使用了 Web 框架,使用框架自帶的快取功能會更方便。

python記憶體快取分類

兼顧速度和成本以及實現的複雜度,最終決定使用記憶體快取,在 Python 中,記憶體快取元件有許多選擇,每種都有其特定的優點和適用場景。以下是一些常見的記憶體快取元件:

1. functools.lru_cache

lru_cache 是 Python 標準庫中的一個裝飾器,用於快取函式的返回結果,基於最近最少使用(LRU)策略。

from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_function(param1, param2):
    # 進行一些耗時的操作
    return result

2. cachetools

cachetools 是一個第三方庫,提供了多種快取策略,包括 LRU、LFU、TTL(基於時間的快取)等。

from cachetools import LRUCache, cached

cache = LRUCache(maxsize=100)

@cached(cache)
def expensive_function(param1, param2):
    # 進行一些耗時的操作
    return result

3. django.core.cache

如果使用 Django 框架,Django 自帶了快取框架,支援多種快取後端,包括記憶體快取。

settings.py 中配置記憶體快取:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'unique-snowflake',
    }
}

4. Flask-Caching

如果使用 Flask 框架,Flask-Caching 外掛可以方便地實現記憶體快取。

from flask import Flask
from flask_caching import Cache

app = Flask(__name__)
cache = Cache(app, config={'CACHE_TYPE': 'simple'})

@app.route('/expensive')
@cache.cached(timeout=60)
def expensive_function():
    # 進行一些耗時的操作
    return result

5. requests_cache

requests_cache 是一個專門用於快取 HTTP 請求的庫,支援多種快取後端,包括記憶體快取。

import requests
import requests_cache

requests_cache.install_cache('demo_cache', backend='memory', expire_after=3600)

response = requests.get('https://api.example.com/data')

6. dogpile.cache

dogpile.cache 是一個更高階的快取庫,提供了靈活的快取後端和快取失效策略。

from dogpile.cache import make_region

region = make_region().configure(
    'dogpile.cache.memory',
    expiration_time=3600
)

@region.cache_on_arguments()
def expensive_function(param1, param2):
    # 進行一些耗時的操作
    return result

7. joblib.Memory

joblib.Memory 常用於科學計算和資料處理領域,用於快取函式的計算結果。

from joblib import Memory

memory = Memory(location='/tmp/joblib_cache', verbose=0)

@memory.cache
def expensive_function(param1, param2):
    # 進行一些耗時的操作
    return result

總結

根據具體需求和使用場景選擇合適的記憶體快取元件。對於簡單的快取需求,可以使用 functools.lru_cachecachetools。對於 Web 應用,django.core.cacheFlask-Caching 是不錯的選擇。對於 HTTP 請求快取,可以使用 requests_cache。對於科學計算,joblib.Memory 是一個好選擇。

cachetools使用

我的專案是一個命令列執行的專案,綜合考量最終決定選擇cachetools

  1. 安裝 cachetools
pip install cachetools
  1. 實現快取工具類
from cachetools import LRUCache
from cachetools import Cache
from siada.cr.logger.logger import logger


class CacheUtils:
    """
    快取工具類
    """

    def __init__(self, cache: Cache = None):
        self.cache = cache if cache else LRUCache(maxsize=100)

    def get_value(self, cache_key: str):
        value = self.cache.get(cache_key, None)
        if value is not None:
            logger.info(f"Cache hit for key: {cache_key}")
        else:
            logger.info(f"Cache miss for key: {cache_key}")
        return value

    def set_key_value(self, cache_key: str, value):
        self.cache[cache_key] = value
        logger.info(f"Set cache key: {cache_key} with value: {value}")

    def set_key_list(self, cache_key: str, value):
        v = self.cache.get(cache_key, None)
        if v is not None:
            v.append(value)
        else:
            self.cache[cache_key] = [value]

    def clear_cache(self):
        self.cache.clear()


# TODO 如果後續生成過程改為多執行緒併發,需考慮資料競爭問題
cache = CacheUtils()

更多驚喜

我還將定期分享:

  • 最新網際網路資訊:讓你時刻掌握行業動態。

  • AI前沿新聞:緊跟技術潮流,不斷提升自我。

  • 技術分享與職業發展:助你在職業生涯中走得更遠、更穩。

  • 程式設計師生活趣事:讓你在忙碌的工作之餘找到共鳴與樂趣。

關注回覆【1024】驚喜等你來拿!

點選檢視驚喜

敬請關注【程式設計師世傑】

點選關注程式設計師世傑

相關文章