cachetools
GitHub Overview
tkem/cachetools
Extensible memoizing collections and decorators
Topics
Star History
Cache Library
cachetools
Overview
cachetools is a Python library that provides extensible memoizing collections and decorators.
Details
cachetools is a library that extends Python's standard library functools.lru_cache to provide more diverse caching strategies. Developed by Thomas Kemmer in 2015, it offers a collection of memoizing collections and decorators. The library implements various cache algorithms including LRU (Least Recently Used), FIFO (First In First Out), TTL (Time To Live), LFU (Least Frequently Used), and TLRU, allowing you to choose the optimal caching strategy for your use case. The @cached decorator makes it easy to add caching functionality to existing functions, while @cachedmethod enables caching for class methods. All cache classes derive from collections.MutableMapping, providing dictionary-like usage with maxsize and currsize properties for cache size management. The library supports custom key functions through hashkey and methodkey, and can be made thread-safe when combined with lock objects.
Pros and Cons
Pros
- Diverse Cache Algorithms: Rich selection including LRU, FIFO, TTL, LFU, TLRU
- Decorator Support: Easy caching with @cached and @cachedmethod decorators
- Standard Library Compatibility: Compatible with functools.lru_cache
- Custom Key Functions: hashkey and methodkey for custom key generation
- Thread Safety: Safe concurrent processing when combined with lock objects
- Statistics: cache_info() for monitoring hit rates and miss counts
- Memory Efficiency: Automatic expiration and size limits for memory management
Cons
- Thread Safety: Not thread-safe by default
- Memory Usage: High memory consumption when caching large amounts of data
- Configuration Complexity: Need to select optimal strategy from multiple cache options
- TTL Precision: Strict time control can be challenging with TTL caches
- Dependencies: Cost of introducing an external library
Key Links
- cachetools Official Documentation
- PyPI - cachetools
- GitHub - tkem/cachetools
- cachetools API Reference
Usage Examples
Basic Caching
from cachetools import cached, LRUCache, TTLCache
# Basic memoization using dictionary
@cached(cache={})
def fibonacci(n):
"""Cache Fibonacci sequence calculation"""
return n if n < 2 else fibonacci(n - 1) + fibonacci(n - 2)
# Usage example
print(fibonacci(100)) # Takes time on first calculation
print(fibonacci(100)) # Fast retrieval from cache
LRU Cache
import urllib.request
from cachetools import cached, LRUCache
# Cache API responses with LRU cache
@cached(cache=LRUCache(maxsize=32))
def get_pep_document(num):
"""Fetch Python Enhancement Proposal"""
url = f'http://www.python.org/dev/peps/pep-{num:04d}/'
with urllib.request.urlopen(url) as response:
return response.read()
# Usage example
pep1 = get_pep_document(1)
pep8 = get_pep_document(8)
TTL Cache (Time-based Expiration)
from cachetools import cached, TTLCache
import requests
# API call cached for 10 minutes
@cached(cache=TTLCache(maxsize=1024, ttl=600))
def get_weather_data(city):
"""Cache weather data with expiration"""
url = f'http://api.openweathermap.org/data/2.5/weather?q={city}'
response = requests.get(url)
return response.json()
# Usage example
weather = get_weather_data("Tokyo")
Cache Statistics
from cachetools import cached, LRUCache
@cached(cache=LRUCache(maxsize=32), info=True)
def expensive_function(x):
"""Simulate expensive computation"""
import time
time.sleep(0.1)
return x * x
# Check statistics
for i in [1, 2, 3, 1, 2, 1]:
result = expensive_function(i)
print(expensive_function.cache_info())
# CacheInfo(hits=3, misses=3, maxsize=32, currsize=3)
Class Method Caching
from cachetools import cachedmethod, LRUCache
from cachetools.keys import hashkey
import requests
class APIClient:
def __init__(self, cache_size=100):
self.cache = LRUCache(maxsize=cache_size)
@cachedmethod(lambda self: self.cache)
def fetch_user_data(self, user_id):
"""Fetch user data with caching"""
response = requests.get(f'/api/users/{user_id}')
return response.json()
@cachedmethod(lambda self: self.cache)
def fetch_post_data(self, post_id):
"""Fetch post data with caching"""
response = requests.get(f'/api/posts/{post_id}')
return response.json()
# Usage example
client = APIClient()
user = client.fetch_user_data(123)
posts = client.fetch_post_data(456)
Custom Key Function
from cachetools import cached, LRUCache
from cachetools.keys import hashkey
def custom_key(*args, config={}, **kwargs):
"""Custom key generation including dictionaries"""
key = hashkey(*args, **kwargs)
# Convert dictionary to sorted tuple
key += tuple(sorted(config.items()))
return key
@cached(LRUCache(maxsize=128), key=custom_key)
def process_data(data, config={}):
"""Data processing with configuration"""
return f"Processed {data} with {config}"
# Usage example
result1 = process_data("data1", config={"format": "json", "sort": True})
result2 = process_data("data1", config={"sort": True, "format": "json"}) # Same key
Shared Cache Across Functions
from cachetools import cached
from cachetools.keys import hashkey
from functools import partial
# Shared cache
shared_cache = {}
@cached(shared_cache, key=partial(hashkey, 'fibonacci'))
def fibonacci(n):
return n if n < 2 else fibonacci(n - 1) + fibonacci(n - 2)
@cached(shared_cache, key=partial(hashkey, 'lucas'))
def lucas_numbers(n):
return 2 - n if n < 2 else lucas_numbers(n - 1) + lucas_numbers(n - 2)
# Both functions use the same cache
fib_result = fibonacci(10)
lucas_result = lucas_numbers(10)
print(f"Shared cache size: {len(shared_cache)}")