How to Implement a Stack Class in Python
A complete Stack class implemented with a Python list, featuring push, pop, peek, is_empty, size, and a readable string representation.
Python code
45 linesclass Stack:
def __init__(self):
self._items = []
def push(self, item):
"""Add an item to the top of the stack."""
self._items.append(item)
def pop(self):
"""Remove and return the top item. Raises IndexError if empty."""
if self.is_empty():
raise IndexError("pop from empty stack")
return self._items.pop()
def peek(self):
"""Return the top item without removing it. Raises IndexError if empty."""
if self.is_empty():
raise IndexError("peek from empty stack")
return self._items[-1]
def is_empty(self):
"""Check if the stack is empty."""
return len(self._items) == 0
def size(self):
"""Return the number of items in the stack."""
return len(self._items)
def __str__(self):
"""String representation showing stack from bottom to top."""
return f"Stack({self._items})"
if __name__ == "__main__":
stack = Stack()
stack.push(10)
stack.push(20)
stack.push(30)
print(f"Stack after pushes: {stack}")
print(f"Size: {stack.size()}")
print(f"Is empty: {stack.is_empty()}")
print(f"Peek: {stack.peek()}")
print(f"Pop: {stack.pop()}")
print(f"Stack after pop: {stack}")
print(f"Final size: {stack.size()}")
Output
Stack after pushes: Stack([10, 20, 30])
Size: 3
Is empty: False
Peek: 30
Pop: 30
Stack after pop: Stack([10, 20])
Final size: 2
How it works
The Stack class uses a private list _items to store elements, with append and pop providing O(1) add/remove at the end, which acts as the top. The push method adds to the end, while pop removes from the end, maintaining LIFO order. The __str__ method returns a readable representation with the bottom-left convention. The guard clauses in pop and peek raise clear IndexError messages to aid debugging. The if __name__ == "__main__" block demonstrates usage without interfering with imports.
Common mistakes
- Popping or peeking from an empty stack without checking `is_empty`, causing an unclear default error.
- Using `insert(0, ...)` for push, which makes operations O(n) instead of O(1).
- Exposing `_items` directly, allowing external code to break the stack invariant.
- Forgetting to reset the stack when reusing an instance, leading to stale data.
Variations
- Use `collections.deque` for thread-safe or double-ended operations.
- Add a `__len__` method so `len(stack)` works instead of `stack.size()`.
Real-world use cases
- Implementing undo/redo functionality in text editors or image tools.
- Parsing expressions and validating parentheses in compilers and linters.
- Managing function call stacks in interpreters or tracing recursion depth.
Sponsored
More from OOP & classes
- Add property getter setter validation in Python easy
- Binary Tree Inorder Traversal in Python easy
- Borg pattern shared state in Python medium
- Bridge Pattern in Python: Separate Abstraction from Implementation medium
- Composable Predicates with the &, |, ~ Operators in Python medium
- Composition over Inheritance: How to Build a Wallet Account in Python easy
Keep learning
Related tutorials and quizzes for this topic.