--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/E5Utilities/E5Cache.py Tue Oct 03 19:37:44 2017 +0200 @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing classes used for caching objects. +""" + +from __future__ import unicode_literals + + +class E5Cache(object): + """ + Class implementing a LRU cache of a specific size. + + If the maximum number of entries is exceeded, the least recently used item + is removed from the cache. A cache hit moves the entry to the front of the + cache. + """ + def __init__(self, size=100): + """ + Constructor + + @param size maximum number of entries that may be stored in the cache + @type int + """ + assert size >= 0 + + self.__size = size + + # internal objects + self.__keyList = [] + self.__store = {} + self.__hits = 0 + self.__misses = 0 + self.__maxsize = 0 + + def __moveLast(self, key): + """ + Private method to move a cached item to the MRU position. + + @param key key of the item to be retrieved + @type any hashable type that can be used as a dict key + """ + self.__keyList.remove(key) + self.__keyList.append(key) + + def __adjustToSize(self): + """ + Private method to adjust the cache to its size. + """ + while len(self.__keyList) > self.__size: + key = self.__keyList.pop(0) + del self.__store[key] + + def getSize(self): + """ + Public method to get the size of the cache. + + @return maximum number of entries of the cache + @rtype int + """ + return self.__size + + def setSize(self, newSize): + """ + Public method to change the size of the cache. + + @param newSize maximum number of entries that may be stored in the + cache + @type int + """ + assert newSize >= 0 + + self.__size = newSize + self.__adjustToSize() + + def get(self, key): + """ + Public method to get an entry from the cache given its key. + + If the key is present in the cache, it is moved to the MRU position. + + @param key key of the item to be retrieved + @type any hashable type that can be used as a dict key + @return cached item for the given key or None, if the key is not + present + @rtype object or None + """ + if key in self.__store: + self.__hits += 1 + self.__moveLast(key) + return self.__store[key] + else: + self.__misses += 1 + return None + + def add(self, key, item): + """ + Public method to add an item to the cache. + + If the key is already in use, the cached item is replaced by the new + one given and is moved to the MRU position + + @param key key of the item to be retrieved + @type any hashable type that can be used as a dict key + @param item item to be cached under the given key + @type object + """ + if key in self.__store: + self.__moveLast(key) + else: + self.__keyList.append(key) + self.__store[key] = item + + self.__adjustToSize() + + self.__maxsize = max(self.__maxsize, len(self.__keyList)) + + def remove(self, key): + """ + Public method to remove an item from the cache. + + @param key key of the item to be retrieved + @type any hashable type that can be used as a dict key + """ + if key in self.__store: + del self.__store[key] + self.__keyList.remove(key) + + def clear(self): + """ + Public method to clear the cache. + """ + self.__keyList = [] + self.__store = {} + + def reset(self): + """ + Public method to reset the cache. + + This is like clear() but sets the various counters to their initial + value as well. + """ + self.clear() + self.__hits = 0 + self.__misses = 0 + self.__maxsize = 0 + + def length(self): + """ + Public method to get the current length of the cache. + + @return current length of the cache + @rtype int + """ + return len(self.__keyList) + + def info(self): + """ + Public method to get some information about the cache. + + @return dictionary containing the cache info + @rtype dict (with keys "hits", "misses", "maxsize", "currsize") + """ + return { + "hits": self.__hits, + "misses": self.__misses, + "maxsize": self.__maxsize, + "currsize": self.length(), + }