Skip to content

Pythonic implementation of LRU Cache #4630

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 30, 2023
100 changes: 100 additions & 0 deletions other/lru_cache_pythonic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from typing import Any, Hashable

'''
The following implementation of LRU Cache is one of the most elegant pythonic implementations.
It only uses the in-built python dictionary (https://docs.python.org/3/library/stdtypes.html#typesmapping). This works because
the Python dictionary maintains the order of insertion of keys and ensures O(1) operations on insert, delete and access.
'''
class LRUCache(dict):
def __init__(self, capacity : int)->None:
'''
Initialize an LRU Cache with given capacity.
capacity : int -> the capacity of the LRU Cache
>>> cache = LRUCache(2)
>>> cache
{}
'''
self.remaining:int = capacity

def get(self, key:Hashable)->Any:
'''
This method gets the value associated with the key.
key : Hashable -> a hashable object that is mapped to a value inside of the LRU cache.
returns -> value : Any -> any object that is stored as a value inside of the LRU cache.

>>> cache = LRUCache(2)
>>> cache.put(1,1)
>>> cache.get(1)
1
>>> cache.get(2)
Traceback (most recent call last):
...
KeyError: '2 not found.'
'''
if key not in self:
raise KeyError(f"{key} not found.")
val = self.pop(key) # Pop the key-value and re-insert to maintain the order
self[key] = val
return val


def put(self, key:Hashable, value:Any)->None:
'''
This method puts the value associated with the key provided inside of the LRU cache.
key : Hashable -> a hashable object that is mapped to a value inside of the LRU cache.
value: Any -> any object that is to be associated with the key inside of the LRU cache.
>>> cache = LRUCache(2)
>>> cache.put(3,3)
>>> cache
{3: 3}
>>> cache.put(2,2)
>>> cache
{3: 3, 2: 2}
'''
# To pop the last value inside of the LRU cache
if key in self:
self.pop(key)
self[key] = value
return

if self.remaining > 0: self.remaining -= 1
# To pop the least recently used item from the dictionary
else: self.pop(next(iter(self)))
self[key] = value

def main()->None:
'''Example test case with LRU_Cache of size 2'''
cache = LRUCache(2) # Creates an LRU cache with size 2
cache.put(1,1) # cache = {1:1}
cache.put(2,2) # cache = {1:1, 2:2}
try:
print(cache.get(1)) # Prints 1
except KeyError:
print("Key not found in cache")
cache.put(3,3) # cache = {1:1, 3:3} key=2 is evicted because it wasn't used recently
try:
print(cache.get(2))
except KeyError:
print("Key=2 not found in cache") # Prints key not found
cache.put(4,4) # cache = {4:4, 3:3} key=1 is evicted because it wasn't used recently
try:
print(cache.get(1))
except KeyError:
print("Key=1 not found in cache") # Prints key not found
try:
print(cache.get(3)) # Prints value 3
except KeyError:
print("Key not found in cache")

try:
print(cache.get(4)) # Prints value 4
except KeyError:
print("Key not found in cache")



if __name__ == '__main__':
import doctest
doctest.testmod()
main()