How to Cache Function Results in Redis with Python
A Python decorator that caches function results in Redis using TTL, with optional fakeredis for testing without a server.
pip install redis
Python code
45 linesimport redis
import json
import time
try:
import fakeredis
except ImportError:
fakeredis = None
from functools import wraps
def cache_redis(cache_key_prefix="cache", ttl=60):
"""Decorator to cache function results in Redis."""
if fakeredis:
r = fakeredis.FakeStrictRedis()
else:
r = redis.Redis(host="localhost", port=6379, db=0)
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
key = f"{cache_key_prefix}:{func.__name__}:{args}:{kwargs}"
cached = r.get(key)
if cached:
print(f"Cache hit for {key}")
return json.loads(cached)
result = func(*args, **kwargs)
r.setex(key, ttl, json.dumps(result))
print(f"Cache miss, stored for {key}")
return result
return wrapper
return decorator
@cache_redis()
def get_user_data(user_id):
"""Fetch user data (simulated slow operation)."""
time.sleep(1) # Simulate expensive database query
return {"id": user_id, "name": f"User {user_id}", "active": True}
if __name__ == "__main__":
print("First call:", get_user_data(101))
print("Second call:", get_user_data(101))
print("Different ID:", get_user_data(102))
Output
Cache miss, stored for cache:get_user_data:(101,):{}
First call: {'id': 101, 'name': 'User 101', 'active': True}
Cache hit for cache:get_user_data:(101,):{}
Second call: {'id': 101, 'name': 'User 101', 'active': True}
Cache miss, stored for cache:get_user_data:(102,):{}
Different ID: {'id': 102, 'name': 'User 102', 'active': True}
How it works
The decorator cache_redis wraps each function and builds a unique cache key from the function name, arguments, and kwargs. On the first call, it computes the result and stores it with setex (set with expiration). Subsequent calls with the same arguments return the cached JSON-parsed value instead of re-executing the function. The @wraps decorator preserves the original function's metadata, and fakeredis allows testing without a real Redis server. Setting a TTL prevents stale data from persisting forever.
Common mistakes
- Not installing the `redis` package via `pip install redis` before importing.
- Using `r.set()` without TTL, causing cached data to never expire.
- Forgetting to use `json.dumps`/`json.loads` for storing dict results.
- Building a key without including arguments, causing collisions between different calls.
Variations
- Use `pickle` instead of JSON to cache complex Python objects, though it's not human-readable.
- Cache with an explicit TTL override per function by adding a parameter to the decorator.
Real-world use cases
- Caching database query results to reduce load on the database in high-traffic web apps.
- Storing API responses from slow external services to improve response times in microservices.
- Caching computed recommendations or pricing in e-commerce to avoid recalculating for every request.
Sponsored
More from Caching & Redis
- Cache Asides in Python with a Read-Through Loader easy
- Cache Data in Redis with Python easy
- Cache Penetration Null Object Mock in Python medium
- Cache Stampede Prevention with SingleFlight in Python medium
- Cache Warming with Python: Preload Hot Keys easy
- Coalescing duplicate in-flight requests: one shared result for concurrent callers hard
Keep learning
Related tutorials and quizzes for this topic.