def test_insert_end(self): """Test insertion at the end of a list.""" linked_list = DoublyLinkedList() new_node = Node(1) linked_list.insert_end(new_node) self.assertEqual(new_node, linked_list.first_node) self.assertEqual(new_node, linked_list.last_node) self.assertEqual(new_node.object, linked_list.first_node.object)
def test_remove(self): """Test removing of a node from a list.""" linked_list = DoublyLinkedList() first_node = Node(1) linked_list.insert_beginning(first_node) node_to_remove = Node(2) linked_list.insert_after(first_node, node_to_remove) linked_list.insert_end(Node(3)) linked_list.remove(node_to_remove) self.assertListItemsEqual(linked_list, [1, 3])
def __init__(self, cache_size): """Initialize cache. :param cache_size: Maximum cache size. """ super(LruCache, self).__init__() self._size = cache_size self._age_cnt = 0 self._storage = {} # linked list is used to store items ordered by age # this allows significantly decrease cost of searching LRU item self._items_sorted_by_age = DoublyLinkedList()
def test_insert_before(self): """Test insertion before a node.""" linked_list = DoublyLinkedList() linked_list.insert_beginning(Node(1)) node = linked_list.first_node for i in range(2, 4): new_node = Node(i) linked_list.insert_before(node, new_node) node = new_node self.assertListItemsEqual(linked_list, [3, 2, 1]) new_node = Node(10) linked_list.insert_before(linked_list.last_node, new_node) self.assertListItemsEqual(linked_list, [3, 2, 10, 1])
def test_insert_beginning(self): """Test insertion at the beginning of a list.""" linked_list = DoublyLinkedList() first_node = Node(1) linked_list.insert_beginning(first_node) self.assertEqual(first_node, linked_list.first_node) self.assertEqual(first_node, linked_list.last_node) self.assertEqual(first_node.object, linked_list.first_node.object) next_first_node = Node(2) linked_list.insert_beginning(next_first_node) self.assertEqual(next_first_node, linked_list.first_node) self.assertEqual(first_node, linked_list.last_node)
def test_creation_from_list(self): """Test list creation.""" linked_list = DoublyLinkedList() self.assertIsNone(linked_list.first_node) self.assertIsNone(linked_list.last_node)
class LruCache(object): """LRU cache. The class implements LRU cache mechanism (https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU). Usage example: .. code-block:: python >>> cache = LruCache(2) >>> >>> cache['key1'] = 'value1' >>> cache['key2'] = 'value2' >>> cache['key3'] = 'value3' >>> >>> assert cache['key2'] == 'value2' >>> assert cache['key3'] == 'value3' """ __slots__ = ('_age_cnt', '_storage', '_size', '_items_sorted_by_age') def __init__(self, cache_size): """Initialize cache. :param cache_size: Maximum cache size. """ super(LruCache, self).__init__() self._size = cache_size self._age_cnt = 0 self._storage = {} # linked list is used to store items ordered by age # this allows significantly decrease cost of searching LRU item self._items_sorted_by_age = DoublyLinkedList() def __setitem__(self, key, value): """Put a value under given key in cache. :param key: A key under which the value must be put in cache. :param value: A value to be put in cache. """ if key in self._storage: # if key is in cache self._update_item(key, value) else: # if key is not in cache self._add_item(key, value) def _update_item(self, key, value): """Update a value in cache under given key. :param key: Key under which an item must be updated. :param value: New value to be put in cache. """ node = self._storage[key] node.object.value = value self._update_age(node) def _add_item(self, key, value): """Add new value into cache under given key. :param key: Key under which a value will be added to cache. :param value: Value to be put in cache. :return: """ if len(self._storage) >= self._size: # if overflow of capacity # find a key of least recently used item lru_item_key = self._find_lru_item_key() if lru_item_key is not None: self._remove_item(lru_item_key) # create new cache object cache_obj = CacheObj(age=None, key=key, value=value) node = Node(cache_obj) self._set_age(node) # add new item to cache self._storage[key] = node def _remove_item(self, key): """Remove a value from cache under given key. :param key: Key under which a value must be removed. """ node = self._storage[key] del self._storage[key] self._items_sorted_by_age.remove(node) def _update_age(self, node): """Update age of given cache object. It sets the next available age to a given object. :param node: A node with cache object which age must be updated. """ self._items_sorted_by_age.remove(node) self._set_age(node) def _set_age(self, node): """Set age of given cache object. It sets the next available age to a given object. :param node: A node with cache object which age must be set. """ next_age = self._get_next_age() node.object.age = next_age self._items_sorted_by_age.insert_end(node) def _get_next_age(self): """Get next age value. Each time the method is executed age counter is incremented by 1. :return: Next age value. """ self._age_cnt += 1 return self._age_cnt def _find_lru_item_key(self): """Find a key of least recently used value. :return: A key or None if no key is found. The latter can happen if cache is empty. """ node = self._items_sorted_by_age.first_node return node.object.key if node else None def __contains__(self, key): """Has cache a value under given key or not. :param key: A key to check. :return: True if cache has a value under given key, False otherwise. """ return key in self._storage def __getitem__(self, key): """Get a value under the given key from cache. :param key: A key under which a result is needed. :raise KeyError: If there is no value in cache under the given key. :return: Value from cache. """ if key in self._storage: node = self._storage[key] self._update_age(node) return node.object.value raise KeyError def __len__(self): """Get amount of values in cache.""" return len(self._storage) def items(self): """Get generator that iterates over items in cache. Each returned variable is a tuple of (key, value). Items are returned from least to most recently used. :return: Generator. """ node = self._items_sorted_by_age.first_node while node is not None: cache_obj = node.object yield cache_obj.key, cache_obj.value node = node.next