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.

Medium Python 3.9+ Aug 9, 2026 Caching & Redis 12 views 0 copies

Python code

53 lines
Python 3.9+
class 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

stdout
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

  1. Use `collections.OrderedDict` to break frequency ties in insertion order (FIFO)
  2. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Caching & Redis

Related tutorials and quizzes for this topic.