Source code for lfu_cache.dllist
from typing import Any, Optional, Union, Iterable
from .exceptions import OtherListElement
[docs]class Element(object):
def __init__(self, value: Any, dllist: 'DLList'):
"""Element object is an element of the linked list. value
keep track of the actual value that is set by the application.
:param value: Actual value set by application.
:param dllist: DLList of which this element is part of
"""
#: Value in the element
self.value: Any = value
#: Link to previous element in the linked list
self.prev: Optional[Element] = None
#: Link to next element in the linked list
self.next: Optional[Element] = None
#: Reference to linked list the element is part of
self.dllist: 'DLList' = dllist
[docs] def nextelement(self) -> Optional['Element']:
"""Return the next list element or None"""
p: Optional[Element] = self.next
if p and p != p.dllist.root:
return p
return None
[docs] def prevelement(self) -> Optional['Element']:
"""Return the next list element or None"""
p: Optional[Element] = self.prev
if p and p != p.dllist.root:
return p
return None
[docs]class DLList(object):
"""
Object implementing a double linked list. Python verison of Golang list
container. To simplify implementation, internally the list is implemented
as a ring such that DLLlist.root is both next element of last and previous
element of first
"""
def __init__(self):
#: Sentinel node of the list.
self.root: Element = self._init()
self.len: int = 0
def _init(self) -> Element:
"""Creates an element for sentinel node"""
e: Element = Element(None, self)
e.next = e
e.prev = e
return e
def __len__(self):
return self.len
def __iter__(self) -> Iterable[Element]:
return self._iter(True)
def _iter(self, value: bool) -> Iterable[Union[Element, Any]]:
"""Helper wrapper for both iter and iternodes"""
front = self.front()
while front:
if value:
yield front.value
else:
yield front
front = front.nextelement()
def _insert(self, e: Element, at: Element) -> Element:
"""Inserts e after at and increase len"""
if e.dllist != self:
raise OtherListElement(self, e)
if at.dllist != self:
raise OtherListElement(self, at)
e.prev = at
e.next = at.next
e.prev.next = e
if e.next:
e.next.prev = e
self.len = self.len + 1
return e
def _insertValue(self, value: Any, at: Element) -> Element:
"""Convenience wrapper for insert(Element(value), at)"""
return self._insert(Element(value, self), at)
def _remove(self, e: Element) -> Element:
"""Removes e from its list, decrements l.len, and return e"""
if e.prev:
e.prev.next = e.next
if e.next:
e.next.prev = e.prev
e.next = None
e.prev = None
e.dllist = DLList()
self.len = self.len - 1
return e
def _move(self, e: Element, at: Element) -> Element:
"""Moves e to next to at and return e"""
if e == at:
return e
if e.prev:
e.prev.next = e.next
if e.next:
e.next.prev = e.prev
e.prev = at
e.next = at.next
if e.prev:
e.prev.next = e
if e.next:
e.next.prev = e
return e
[docs] def reinit(self) -> None:
"""Reinits list"""
self.root = self._init()
self.len = 0
[docs] def front(self) -> Optional[Element]:
"""return first element of list or None"""
if not self.len:
return None
return self.root.next
[docs] def back(self) -> Optional[Element]:
"""return last element of list or None"""
if not self.len:
return None
return self.root.prev
[docs] def appendleft(self, value: Any) -> Element:
"""Inserts a new element with value v at front of list. return Element
:param value: Value to be saved
:return: New element with value value
"""
return self._insertValue(value, self.root)
[docs] def append(self, value: Any) -> Element:
"""Inserts a new element with value v at tail of list. return Element
:param value: Value to be saved
:return: New element with value value
"""
if self.root.prev:
return self._insertValue(value, self.root.prev)
raise RuntimeError("Should not reach here")
[docs] def remove(self, e: Element) -> Any:
"""Remove element e and return value associated with it"""
if e.dllist == self:
self._remove(e)
return e.value
[docs] def insertbefore(self, value: Any, mark: Element) -> Element:
"""Inserts a new element e before mark and return e
:param value: value to be saved
:param mark: new element is added before mark
"""
if mark.prev:
return self._insertValue(value, mark.prev)
raise RuntimeError("Should not reach here")
[docs] def insertafter(self, value: Any, mark: Element) -> Element:
"""Inserts a new element e after mark and return e
:param value: value to be saved
:param mark: new element is added after mark
"""
return self._insertValue(value, mark)
[docs] def movetofront(self, e: Element) -> None:
"""Moves element e to front of list
:param e: Element to move"""
if e.dllist != self or self.root.next == e:
return
self._move(e, self.root)
[docs] def movetoback(self, e: Element) -> None:
"""Moves element e to tail of list
:param e: Element to move"""
if e.dllist != self or self.root.prev == e:
return
if self.root.prev:
self._move(e, self.root.prev)
return
raise RuntimeError("Should not reach here")
[docs] def movebefore(self, e: Element, mark: Element) -> None:
"""Moves element e before mark
:param e: Element to move
:param mark: Element before which e will be moved"""
if e.dllist != self or e == mark or mark.dllist != self:
return
if mark.prev:
self._move(e, mark.prev)
return
raise RuntimeError("Should not reach here")
[docs] def moveafter(self, e: Element, mark: Element) -> None:
"""Move element e after mark
:param e: Element to move
:param mark: Element after which e will be moved"""
if e.dllist != self or e == mark or mark.dllist != self:
return
self._move(e, mark)
def iternodes(self) -> Iterable[Element]:
return self._iter(False)