115 lines
3.1 KiB
Python
115 lines
3.1 KiB
Python
|
|
from typing import List, Optional, TypeVar
|
||
|
|
|
||
|
|
T = TypeVar("T")
|
||
|
|
|
||
|
|
|
||
|
|
class Stack(List[T]):
|
||
|
|
_max_length: Optional[int]
|
||
|
|
|
||
|
|
def __init__(self, *args, max_length: Optional[int] = None):
|
||
|
|
initial_entries = args
|
||
|
|
if max_length:
|
||
|
|
initial_entries = initial_entries[:max_length]
|
||
|
|
super().__init__(initial_entries)
|
||
|
|
self._max_length = max_length
|
||
|
|
|
||
|
|
def empty(self) -> bool:
|
||
|
|
"""Tests if this stack is empty."""
|
||
|
|
return len(self) == 0
|
||
|
|
|
||
|
|
def peek(self) -> Optional[T]:
|
||
|
|
"""Looks at the object at the top (last/most recently added) of this
|
||
|
|
stack without removing it from the stack."""
|
||
|
|
return self.at(-1)
|
||
|
|
|
||
|
|
def pop(self) -> Optional[T]:
|
||
|
|
"""Removes the object at the top of this stack and returns that object
|
||
|
|
as the value of this function."""
|
||
|
|
try:
|
||
|
|
value = super().pop()
|
||
|
|
return value
|
||
|
|
except IndexError:
|
||
|
|
pass
|
||
|
|
|
||
|
|
def push(self, item: T) -> None:
|
||
|
|
"""Pushes an item onto the top of this stack.
|
||
|
|
|
||
|
|
Proxy of List.append
|
||
|
|
|
||
|
|
Limits Stack Length to _max_length entries
|
||
|
|
"""
|
||
|
|
self.append(item)
|
||
|
|
if self._max_length:
|
||
|
|
del self[: -self._max_length]
|
||
|
|
|
||
|
|
def search(self, x: T) -> Optional[int]:
|
||
|
|
"""Returns the 0-based position of the last item whose value is equal
|
||
|
|
to x on this stack.
|
||
|
|
|
||
|
|
We deviate from the typical 1-based position used by Stack
|
||
|
|
classes (i.e. Java) because most python users (and developers in
|
||
|
|
general) are accustomed to 0-based indexing.
|
||
|
|
"""
|
||
|
|
copy = self.copy()
|
||
|
|
copy.reverse()
|
||
|
|
|
||
|
|
try:
|
||
|
|
found_index = copy.index(x)
|
||
|
|
return len(self) - found_index - 1
|
||
|
|
except ValueError:
|
||
|
|
pass
|
||
|
|
|
||
|
|
def at(self, index: int, default: Optional[T] = None) -> Optional[T]:
|
||
|
|
"""Returns the item located at the index.
|
||
|
|
|
||
|
|
If the index does not exist in the stack (Overflow or
|
||
|
|
Underflow), None is returned instead.
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
value = self[index]
|
||
|
|
return value
|
||
|
|
except IndexError:
|
||
|
|
return default
|
||
|
|
|
||
|
|
def copy(self) -> "Stack[T]":
|
||
|
|
"""Returns a copy of the current Stack."""
|
||
|
|
copy = super().copy()
|
||
|
|
return Stack(*copy)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def first(self) -> Optional[T]:
|
||
|
|
"""Returns the first item of the stack without removing it.
|
||
|
|
|
||
|
|
Same as Stack.bottom.
|
||
|
|
"""
|
||
|
|
return self.at(0)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def last(self) -> Optional[T]:
|
||
|
|
"""Returns the last item of the stack without removing it.
|
||
|
|
|
||
|
|
Same as Stack.top.
|
||
|
|
"""
|
||
|
|
return self.at(-1)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def bottom(self) -> Optional[T]:
|
||
|
|
"""Returns the item on the bottom of the stack without removing it.
|
||
|
|
|
||
|
|
Same as Stack.first.
|
||
|
|
"""
|
||
|
|
return self.at(0)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def top(self) -> Optional[T]:
|
||
|
|
"""Returns the item on the top of the stack without removing it.
|
||
|
|
|
||
|
|
Same as Stack.last.
|
||
|
|
"""
|
||
|
|
return self.at(-1)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def length(self) -> int:
|
||
|
|
"""Returns the number of items in the Stack."""
|
||
|
|
return len(self)
|