Caching's category page had no intro, so its meta description fell back to generic text and gave readers no guidance on which library to pick. Co-Authored-By: Claude <noreply@anthropic.com>
4.7 KiB
In memory, on disk, or on a cache server: the Python caching library you want is cachetools, DiskCache, or dogpile.cache. For HTTP responses, it's Hishel.
How to choose:
- Function results in one process's memory, with a size limit or a time-to-live: cachetools
- A cache on local disk that processes on one machine share, with no server to run: DiskCache
- A Django cache backend on local disk: DiskCache
- One cache on memcached or Redis for many processes or servers: dogpile.cache
- HTTP responses in HTTPX or Requests: Hishel
Cache-Controlheaders and response caching in FastAPI or another ASGI app: Hishel- Django querysets, invalidated when a model changes: Cacheops
cachetools offers variants of the standard library's @lru_cache with more cache algorithms, including a TTLCache whose items expire after a time-to-live. Wrap a function in @cached and pass it the cache to use. The cache classes aren't thread-safe, so when threads share a cache, give @cached a threading.Lock. The cache lives in your process's memory, so each worker process of a web app keeps its own copy.
DiskCache is a disk and file backed cache library in pure Python, built on SQLite, with no other process to run. Create a Cache with a directory path: two Cache objects on the same directory can live in separate processes, so the workers on one machine share one cache. Wrap a function in @cache.memoize(), which takes arguments like lru_cache's. In Django, set diskcache.DjangoCache as the cache BACKEND. Keep the directory on a local disk, since SQLite isn't recommended on NFS mounts.
dogpile.cache is a caching API over backends of any variety, memcached and Redis among them. You ask it for a value and hand it a function that creates the value only when needed. When the value expires, one worker regenerates it instead of every worker at once. Create a region with make_region() at import time, decorate functions with @region.cache_on_arguments(), and call configure() later with the backend and expiration time from your config file. When several processes share one Redis or memcached server, turn on the backend's distributed_lock so that lock covers them all.
Hishel caches HTTP responses by RFC 9111, the HTTP caching rules browsers follow, in both sync and async code. With HTTPX, swap your client for Hishel's cache-enabled one, or give a client you already have its cache transport. With Requests, mount its cache adapter on a Session. In FastAPI, a dependency sets Cache-Control headers for browsers and CDNs, and its ASGI middleware also caches responses on your server.
Cacheops caches Django querysets in Redis and invalidates them on save(), delete(), and many-to-many changes. Add it to INSTALLED_APPS and point it at its own Redis database, as its docs highly recommend. Turn caching on per app with 'app_name.*', since '*.*' can also cache tables you don't mean to, like migrations. Call .cache() on a queryset to cache it by hand, and wrap a function in @cached_as(Article) to drop its result whenever an Article changes.
Plan how stale entries leave the cache, too. cachetools' decorators add a cache_clear() function, DiskCache evicts every key with a given tag, and a function decorated by dogpile.cache takes invalidate() with the same arguments you'd call it with.