Now the payoff: a Cache that knows nothing about how eviction is decided β it just holds data and delegates the choice to whatever policy it was handed. The same cache class works with FIFO, LRU, or LFU, and that is the whole point of the Strategy pattern.
get(key) β return the value (and record an access) or None on a missput(key, value) β insert or update; when a new key would exceed capacity, ask the policy for a victim and evict it firstclass EvictionPolicy:
def on_insert(self, key):
raise NotImplementedError
def on_access(self, key):
raise NotImplementedError
def evict(self):
raise NotImplementedError
class FIFOPolicy(EvictionPolicy):
def __init__(self):
self._order = []
def on_insert(self, key):
self._order.append(key)
def on_access(self, key):
pass
def evict(self):
return self._order.pop(0)
class LRUPolicy(EvictionPolicy):
def __init__(self):
self._order = []
def on_insert(self, key):
self._order.append(key)
def on_access(self, key):
if key in self._order:
self._order.remove(key)
self._order.append(key)
def evict(self):
return self._order.pop(0)
class LFUPolicy(EvictionPolicy):
def __init__(self):
self._counts = {}
self._order = []
def on_insert(self, key):
self._counts[key] = 1
self._order.append(key)
def on_access(self, key):
self._counts[key] = self._counts.get(key, 0) + 1
def evict(self):
victim = min(self._order, key=lambda k: self._counts[k])
self._order.remove(victim)
del self._counts[victim]
return victim
class Cache:
def __init__(self, capacity, policy):
self.capacity = capacity
self.policy = policy # any EvictionPolicy
self._store = {}
def get(self, key):
"""Return the value for key, recording an access; None if absent."""
# TODO:
# if key in self._store:
# self.policy.on_access(key)
# return self._store[key]
# return None
pass
def put(self, key, value):
"""Insert or update key; evict via the policy when over capacity."""
# TODO:
# if key in self._store:
# self._store[key] = value
# self.policy.on_access(key)
# return
# if len(self._store) >= self.capacity:
# victim = self.policy.evict()
# del self._store[victim]
# self._store[key] = value
# self.policy.on_insert(key)
pass
The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.
Now the payoff: a Cache that knows nothing about how eviction is decided β it just holds data and delegates the choice to whatever policy it was handed. The same cache class works with FIFO, LRU, or LFU, and that is the whole point of the Strategy pattern.
get(key) β return the value (and record an access) or None on a missput(key, value) β insert or update; when a new key would exceed capacity, ask the policy for a victim and evict it firstPress Run Tests or ββ© to check your solution.