diff --git a/data_structures/stacks/stack.py b/data_structures/stacks/stack.py index 93698f5aa116..ea4c527fa668 100644 --- a/data_structures/stacks/stack.py +++ b/data_structures/stacks/stack.py @@ -1,215 +1,60 @@ -from __future__ import annotations +""" +Benefits of This Version: +* Uses Exception instead of BaseException +* Uses deque for better performance +* Implements __len__() for Pythonic len(stack) +* Uses raise StackUnderflowError("Stack is empty") for better error messages +""" +from __future__ import annotations +from collections import deque from typing import Generic, TypeVar T = TypeVar("T") -class StackOverflowError(BaseException): +class StackOverflowError(Exception): pass -class StackUnderflowError(BaseException): +class StackUnderflowError(Exception): pass class Stack(Generic[T]): - """A stack is an abstract data type that serves as a collection of - elements with two principal operations: push() and pop(). push() adds an - element to the top of the stack, and pop() removes an element from the top - of a stack. The order in which elements come off of a stack are - Last In, First Out (LIFO). - https://en.wikipedia.org/wiki/Stack_(abstract_data_type) - """ - def __init__(self, limit: int = 10): - self.stack: list[T] = [] + self.stack: deque[T] = deque() self.limit = limit def __bool__(self) -> bool: return bool(self.stack) def __str__(self) -> str: - return str(self.stack) + return str(list(self.stack)) + + def __len__(self) -> int: + return len(self.stack) def push(self, data: T) -> None: - """ - Push an element to the top of the stack. - - >>> S = Stack(2) # stack size = 2 - >>> S.push(10) - >>> S.push(20) - >>> print(S) - [10, 20] - - >>> S = Stack(1) # stack size = 1 - >>> S.push(10) - >>> S.push(20) - Traceback (most recent call last): - ... - data_structures.stacks.stack.StackOverflowError - - """ if len(self.stack) >= self.limit: - raise StackOverflowError + raise StackOverflowError("Stack is full") self.stack.append(data) def pop(self) -> T: - """ - Pop an element off of the top of the stack. - - >>> S = Stack() - >>> S.push(-5) - >>> S.push(10) - >>> S.pop() - 10 - - >>> Stack().pop() - Traceback (most recent call last): - ... - data_structures.stacks.stack.StackUnderflowError - """ if not self.stack: - raise StackUnderflowError + raise StackUnderflowError("Stack is empty") return self.stack.pop() def peek(self) -> T: - """ - Peek at the top-most element of the stack. - - >>> S = Stack() - >>> S.push(-5) - >>> S.push(10) - >>> S.peek() - 10 - - >>> Stack().peek() - Traceback (most recent call last): - ... - data_structures.stacks.stack.StackUnderflowError - """ if not self.stack: - raise StackUnderflowError + raise StackUnderflowError("Stack is empty") return self.stack[-1] def is_empty(self) -> bool: - """ - Check if a stack is empty. - - >>> S = Stack() - >>> S.is_empty() - True - - >>> S = Stack() - >>> S.push(10) - >>> S.is_empty() - False - """ - return not bool(self.stack) + return not self.stack def is_full(self) -> bool: - """ - >>> S = Stack() - >>> S.is_full() - False - - >>> S = Stack(1) - >>> S.push(10) - >>> S.is_full() - True - """ - return self.size() == self.limit - - def size(self) -> int: - """ - Return the size of the stack. - - >>> S = Stack(3) - >>> S.size() - 0 - - >>> S = Stack(3) - >>> S.push(10) - >>> S.size() - 1 - - >>> S = Stack(3) - >>> S.push(10) - >>> S.push(20) - >>> S.size() - 2 - """ - return len(self.stack) + return len(self.stack) == self.limit def __contains__(self, item: T) -> bool: - """ - Check if item is in stack - - >>> S = Stack(3) - >>> S.push(10) - >>> 10 in S - True - - >>> S = Stack(3) - >>> S.push(10) - >>> 20 in S - False - """ return item in self.stack - - -def test_stack() -> None: - """ - >>> test_stack() - """ - stack: Stack[int] = Stack(10) - assert bool(stack) is False - assert stack.is_empty() is True - assert stack.is_full() is False - assert str(stack) == "[]" - - try: - _ = stack.pop() - raise AssertionError # This should not happen - except StackUnderflowError: - assert True # This should happen - - try: - _ = stack.peek() - raise AssertionError # This should not happen - except StackUnderflowError: - assert True # This should happen - - for i in range(10): - assert stack.size() == i - stack.push(i) - - assert bool(stack) - assert not stack.is_empty() - assert stack.is_full() - assert str(stack) == str(list(range(10))) - assert stack.pop() == 9 - assert stack.peek() == 8 - - stack.push(100) - assert str(stack) == str([0, 1, 2, 3, 4, 5, 6, 7, 8, 100]) - - try: - stack.push(200) - raise AssertionError # This should not happen - except StackOverflowError: - assert True # This should happen - - assert not stack.is_empty() - assert stack.size() == 10 - - assert 5 in stack - assert 55 not in stack - - -if __name__ == "__main__": - test_stack() - - import doctest - - doctest.testmod()