How to Implement an LFU Cache in Python
Implement a Least Frequently Used (LFU) cache with frequency tracking dictionaries to evict the least accessed items when capacity is reached.
Python code
53 linesclass LFUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.data = {}
self.freq = {}
self.min_freq = 0
def get(self, key: int) -> int:
if key not in self.data:
return -1
self._increment_freq(key)
return self.data[key]
def put(self, key: int, value: int) -> None:
if self.capacity <= 0:
return
if key in self.data:
self.data[key] = value
self._increment_freq(key)
return
if len(self.data) >= self.capacity:
self._evict()
self.data[key] = value
self.freq[key] = 1
self.min_freq = 1
def _increment_freq(self, key: int) -> None:
self.freq[key] += 1
if self.freq[key] == 2:
self.min_freq = 2
def _evict(self) -> None:
lfu_key = None
lfu_freq = float('inf')
for k, f in self.freq.items():
if f < lfu_freq:
lfu_freq = f
lfu_key = k
if lfu_key is not None:
del self.data[lfu_key]
del self.freq[lfu_key]
if self.freq:
self.min_freq = min(self.freq.values())
if __name__ == "__main__":
cache = LFUCache(2)
cache.put(1, 10)
cache.put(2, 20)
print(cache.get(1))
cache.put(3, 30)
print(cache.get(2))
print(cache.get(3))
Output
10
-1
30
How it works
The LFU cache tracks access frequency in a separate freq dictionary, mapping each key to how often it was used. On get or updated put, frequencies increment via _increment_freq. When capacity is exceeded, _evict scans for the key with the smallest frequency — a tie-breaker to the first encountered — and removes it from both data and freq. The min_freq attribute is kept for quick access but is not strictly required for correctness in this simple version. This approach uses O(n) eviction time, which is acceptable for small caches or mock scenarios.
Common mistakes
- Forgetting to increment frequency on get calls, causing stale counts
- Not handling capacity <= 0 gracefully, leading to KeyError on eviction
- Assuming ties in frequency are handled with FIFO or LRU; this version evicts the first found
- Updating the value of an existing key without incrementing its frequency
Variations
- Use `collections.OrderedDict` to break frequency ties in insertion order (FIFO)
- Track frequencies in a min-heap for O(log n) eviction with lazy deletion
Real-world use cases
- Caching database query results where certain rows are queried more often than others.
- Storing recently accessed user preferences in memory for fast retrieval in web apps.
- Building a mock cache for testing eviction logic before scaling to distributed caching systems.
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.