Search code examples
pythoncachingttllru

How to solve issue faced when printing value from a LRU Cache and deleting values after a period of time?


I have been trying to implement my own version of an LRU cache that deletes the least recently used value after a short period of time. I am using the code I found at (https://www.kunxi.org/blog/2014/05/lru-cache-in-python/) as a template.

(This the code found there so you don't have to open it):

class LRUCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.tm = 0
        self.cache = {}
        self.lru = {}

    def get(self, key):
        if key in self.cache:
            self.lru[key] = self.tm
            self.tm += 1
            return self.cache[key]
        return -1

    def set(self, key, value):
        if len(self.cache) >= self.capacity:
            # find the LRU entry
            old_key = min(self.lru.keys(), key=lambda k:self.lru[k])
            self.cache.pop(old_key)
            self.lru.pop(old_key)
        self.cache[key] = value
        self.lru[key] = self.tm
        self.tm += 1

This code works as intended

cache = LRUCache(2)
cache.set(1, 20)
cache.set(2, 40)

print(cache.get(2))
40

but for this case it returns an error:

for i in cache:
    print(i)

Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: 'LRUCache' object is not iterable

the output I expected is:

(1, 20)
(2, 40)

Why doesn't it work for this case and how do I get it to print my expected output?

Also I want to delete values that have been in the cache for long periods of time automatically, I have experimented but couldn't accomplish this. I tried modifying the code such that:

from time import time

class LRUCache:
    def __init__(self, capacity, expiry_time):
        self.capacity = capacity
        self.expiry_time = expiry_time
        self.expired = False
        self.tm = 0
        self.cache = {}
        self.lru = {}

    def get(self, key):
        if key in self.cache:
            self.lru[key] = self.tm
            self.tm += 1
            return self.cache[key]
        return -1
        if self.expired is False:
            return (self.expires_at < time())
        if self.expires_at < time():
            return -1


    def set(self, key, value):
        if len(self.cache) >= self.capacity:
            # find the LRU entry
            old_key = min(self.lru.keys(), key=lambda k:self.lru[k])
            self.cache.pop(old_key)
            self.lru.pop(old_key)
        self.cache[key] = value
        self.lru[key] = self.tm
        self.tm += 1

However even with my changes, when I try to 'get' values, they do not return -1 when it is past the expiry time. How do I get around this?


Solution

  • Why doesn't it work for this case and how do I get it to print my expected output?

    Your cache object is getting the error object is not iterable because you haven't implemented any ways for it to be iterable.

    This is perhaps better left to another question: Build a Basic Python Iterator

    With a quote from this answer:

    There are four ways to build an iterative function:

    • ...
    • ...
    • create an iterator (defines __iter__ and __next__ (or next in Python 2.x))
    • create a function that Python can iterate over on its own (defines __getitem__)

    With my changes, when I try to 'get' values, they do not return -1 when it is past the expiry time

    For your changes, you have placed them at the end of the function, so the default functionality is always happening first. This need re-arraging as so:

    class LRUCache:
        def __init__(self, capacity, expiry_time):
            ...
    
        def get(self, key):
            if self.expired is False:
                return (self.expires_at < time())
            if self.expires_at < time():
                return -1
            if key in self.cache:
                self.lru[key] = self.tm
                self.tm += 1
                return self.cache[key]
            return -1
    
        def set(self, key, value):
            ...
    

    However, there are a couple of other immediate problems with the implementation:

    • self.expiry_time is not used or checked against once set.
    • self.expires_at is never defined.
    • if self.expired is False: this will always report True because expired is never changed.
      • and (once expires_at is defined) will just return True