Open In App

Implementing LRU Cache Decorator in Python

Improve
Improve
Like Article
Like
Save
Share
Report

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.
 

Python3




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 value 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

 



Last Updated : 19 Aug, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads