CSC110 / lectures / ADT / Stack.py
Stack.py
Raw
from typing import Any


# abstract class
class Stack:
    # abstact method
    def is_empty(self) -> bool:
        raise NotImplementedError

    def push(self, item: Any) -> None:
        raise NotImplementedError

    def pop(self) -> Any:
        raise NotImplementedError


# polymorphic code using inheritance
def push_and_pop(stack: Stack, item: Any) -> None:
    stack.push(item)
    stack.pop()


class Stack1(Stack):
    """A last-in-first-out (LIFO) stack of items.

    Stores data in a last-in, first-out order. When removing an item from the
    stack, the most recently-added item is the one that is removed.

    >>> s = Stack1()
    >>> s.is_empty()
    True
    >>> s.push('hello')
    >>> s.is_empty()
    False
    >>> s.push('goodbye')
    >>> s.pop()
    'goodbye'
    """
    # Private Instance Attributes:
    #   - items: a list containing the items in the stack. The end of the list
    #            represents the top of the stack.
    _items: list

    def __init__(self) -> None:
        """Initialize a new empty stack."""
        self._items = []

    def is_empty(self) -> bool:
        """Return whether this stack contains no items.
        """
        return self._items == []

    def push(self, item: Any) -> None:
        """Add a new element to the top of this stack."""
        self._items.append(item)

    def pop(self) -> Any:
        """Remove and return the element at the top of this stack.

        Raise an EmptyStackError if this stack is empty.
        """
        if self.is_empty():
            raise EmptyStackError  # Could also raise a built-in error, like ValueError/IndexError
        else:
            return self._items.pop()

    def __str__(self):
        return 'Test'


class EmptyStackError(Exception):
    """Exception raised when calling pop on an empty stack."""

    # def __str__(self) -> str:
    #     """Return a string representation of this error."""
    #     return 'pop may not be called on an empty stack'


class Stack2:
    # Duplicated code from Stack1 omitted. Only push and pop are different.

    def push(self, item: Any) -> None:
        """Add a new element to the top of this stack.
        """
        self._items.insert(0, item)

    def pop(self) -> Any:
        """Remove and return the element at the top of this stack.

        Preconditions:
            - not self.is_empty()
        """
        return self._items.pop(0)