Skip to content
Related Articles

Related Articles

Implementing LRU Cache Decorator in Python
  • Last Updated : 17 Jul, 2020

LRU is the cache replacement algorithm that removes the least recently used data and stores the new data. Suppose we have a cache space of 10 memory frames. And each frame is filled with a file. Now if we want to store the new file, we need to remove the oldest file in the cache and add the new file. This is how LRU works.

LRU cache consists of Queue and Dictionary data structures.

  • Queue: to store the most recently used to least recently used files
  • Hash table: to store the file and its position in the cache

Refer to the below articles to get more information about the topic:

What is decorator?

A decorator is a function that takes a function as its only parameter and returns a function. This is helpful to “wrap” functionality with the same code over and over again.

Note: For more information, refer to Decorators in Python.



Now, after getting the basic idea about the LRU and Decorators in Python, let’s have a look at the implementation of the LRU cache Decorator in Python.




from collections import deque
  
  
# LRU cache implementation
class LRUCache:
      
    def __init__(self, size=5):
        self.size = size
        self.container = deque()
        self.map = dict() 
       
       
    def reallocate(self):
        # to reallocate the hashmap for
        # every access of file access file
        # will reallocate the data in hashmap
        # according to the numbers position 
        # in the container for every access,
        # hit and miss(evict)
        if len(self.container) > 1:
              
            for key, val in enumerate(self.container):
                self.map[val] = key
       
       
    def access(self, val):
          
        # print("access "+str(val))
        self.container.remove(val)
        self.container.appendleft(val)
        self.reallocate()
       
    def evict(self, val):
          
        # print("cache miss "+str(val))
        if val in self.map:
            #del self.map[val]
            self.container.remove(val)
          
        else:
            x = self.container.pop()
            del self.map[x]
          
        self.normal_insert(val)
           
    def normal_insert(self, val):
        self.container.appendleft(val)
        self.reallocate()
           
    def insert(self, val):
          
        if val in self.map.keys():
              
            # if vlaue in present in
            # the hashmap then it is a hit.
            # access function will access the
            # number already present and replace
            # it to leftmost position
            self.access(val)
              
        else:
            # if value is not present in
            # the hashtable
            if (len(self.map.keys()) == self.size):
                # if the size of the queue
                # is equal to capacity and
                # we try to insert the number,
                # then it is a miss then,
                # evict function will delete the
                # right most elements and insert
                # the latest element in the
                # leftmost position
                self.evict(val)
                  
            else:
                # normal_insert function will normally
                # insert the data into the cache..
                self.normal_insert(val)
       
    def print(self):
        lru_elements = [x for x in self.container]
        print(lru_elements)
   
   
  
# definition of lru decorator
def LRUDecorator(size):
      
    lru = LRUCache(size)
      
    def decorator(function):
          
        def functionality(num):
            lru.insert(num)
            lru.print()
              
            # to check the num pageframe(position)
            # uncomment the below statement
            # print(lur.map)
            print(num, function(num))
        return functionality
      
    return decorator
   
      
# Using LRU Decorator
@LRUDecorator(size=4)
def ex_func_01(n):
    print(f'Computing...{n}')
    time.sleep(1)
    return n
           
print(f'\nFunction: ex_func_01')
print(ex_func_01(1))
print(ex_func_01(2))
print(ex_func_01(3))
print(ex_func_01(4))
print(ex_func_01(1))
print(ex_func_01(2))
print(ex_func_01(5))
print(ex_func_01(1))
print(ex_func_01(2))
print(ex_func_01(3))
print(ex_func_01(4))
print(ex_func_01(5))

Output:

Function: ex_func_01
[1]
Computing...1
1 1
None
[2, 1]
Computing...2
2 2
None
[3, 2, 1]
Computing...3
3 3
None
[4, 3, 2, 1]
Computing...4
4 4
None
[1, 4, 3, 2]
Computing...1
1 1
None
[2, 1, 4, 3]
Computing...2
2 2
None
[5, 2, 1, 4]
Computing...5
5 5
None
[1, 5, 2, 4]
Computing...1
1 1
None
[2, 1, 5, 4]
Computing...2
2 2
None
[3, 2, 1, 5]
Computing...3
3 3
None
[4, 3, 2, 1]
Computing...4
4 4
None
[5, 4, 3, 2]
Computing...5
5 5
None

Attention geek! Strengthen your foundations with the Python Programming Foundation Course and learn the basics.

To begin with, your interview preparations Enhance your Data Structures concepts with the Python DS Course.

My Personal Notes arrow_drop_up
Recommended Articles
Page :