Python Stack: List, deque, and Class Implementations

Quick answer: A Python stack is last-in, first-out. Use list.append() and list.pop() for the common case, collections.deque when you also need efficient operations at both ends, or a small class when you need a domain-specific API.

Python stack infographic comparing list, deque, push and pop operations, LIFO order, and underflow handling
A stack is last-in, first-out: append or push at the top and pop from that same end without shifting older items.

A stack is a last-in, first-out data structure. The newest item is the first one removed. That rule is usually shortened to LIFO. In Python, a stack is useful for undo history, parsing, depth-first search, expression checks, and any workflow where the most recent item should be handled first.

The official Python tutorial shows using lists as stacks with append() and pop(). Python also provides collections.deque, which is a good option when you need efficient appends and pops from both ends.

Build a stack with a list

The simplest Python stack is a list. Use append() to push a new item onto the top, and pop() to remove and return the top item.

stack = []

stack.append("first")
stack.append("second")
stack.append("third")

print(stack.pop())
print(stack.pop())
print(stack)

The output shows third before second because third was added last. That makes a list-backed stack easy to understand and enough for many everyday scripts.

A list is also easy to inspect while learning. The right side of the list is the top of the stack in this pattern, so printed output matches the order of future pops. Keep that convention consistent; mixing left-side and right-side operations makes a stack harder to reason about.

Check before popping

Calling pop() on an empty list raises IndexError. If empty stacks are possible in your workflow, check before removing the top item.

def pop_or_none(stack):
    if stack:
        return stack.pop()
    return None

items = []
print(pop_or_none(items))

items.append("task")
print(pop_or_none(items))

This pattern is useful in loops where work may already be finished. For more detail on truth checks, see the check if a list is empty guide.

Choose the empty-stack behavior deliberately. Returning None is convenient for optional work queues, but raising an exception is better when an empty stack means the caller made a logic error. Do not hide an error that should fail loudly during testing.

Python Pool infographic showing a list stack, append, pop, top item, and LIFO order
append and pop provide simple last-in, first-out stack operations on a list.

Peek at the top item

Sometimes you need to inspect the top item without removing it. Index -1 returns the last item in a non-empty list, which is the stack top.

def peek(stack):
    if not stack:
        return None
    return stack[-1]

history = ["open", "edit", "save"]
print(peek(history))
print(history)

Peeking should not change the stack. That makes it useful for validation, previews, and control-flow decisions before a later pop().

Create a small Stack class

A class is helpful when you want named methods and one clear place for stack behavior. The internal storage can still be a list, but callers use push(), pop(), and peek().

class Stack:
    def __init__(self):
        self._items = []

    def push(self, item):
        self._items.append(item)

    def pop(self):
        if not self._items:
            raise IndexError("pop from empty stack")
        return self._items.pop()

    def peek(self):
        if not self._items:
            return None
        return self._items[-1]

    def __len__(self):
        return len(self._items)

stack = Stack()
stack.push("draft")
stack.push("publish")
print(stack.peek())
print(stack.pop())
print(len(stack))

The class makes error handling explicit. Raising IndexError mirrors list behavior, while returning None from peek() keeps inspection safe. If you are reviewing list growth behavior, the Python dynamic array guide explains how Python lists resize.

For tests, cover push order, pop order, peek without removal, length after each operation, and the empty-pop path. Those cases catch most broken stack implementations because every stack method is small but tightly connected to LIFO behavior.

Python Pool infographic comparing collections.deque, append, pop, and stack operations
deque supports efficient operations at both ends when the workload needs them.

Use deque as a stack

deque supports fast appends and pops and can still behave like a stack when you use one end consistently. Use append() to push and pop() to remove from the right side.

from collections import deque

stack = deque()
stack.append("red")
stack.append("green")
stack.append("blue")

print(stack.pop())
print(stack.pop())
print(stack)

For a pure stack, a list is often enough. Choose deque when your design may also need queue-like behavior or frequent operations on both ends. For priority-based removal instead of LIFO order, read the Python priority queue guide.

The practical difference is mostly about intent and operation patterns. A list is familiar and compact when all work happens at the end. A deque communicates that the container may be used from either side, which matters in codebases where stacks and queues appear together.

Check balanced parentheses

A classic stack use case is matching pairs. Push opening brackets. When a closing bracket appears, pop the most recent opening bracket and check that the pair matches.

def is_balanced(text):
    pairs = {")": "(", "]": "[", "}": "{"}
    stack = []

    for char in text:
        if char in pairs.values():
            stack.append(char)
        elif char in pairs:
            if not stack or stack.pop() != pairs[char]:
                return False

    return not stack

print(is_balanced("{[()]}") )
print(is_balanced("{[(])}"))

This works because every closing bracket must match the most recent unmatched opening bracket. If the stack is empty too early, or if anything remains at the end, the expression is not balanced.

Python Pool infographic showing a Stack class, push, pop, peek, and internal storage
A class can enforce stack invariants and expose a focused push, pop, and peek API.

When to use a stack

Use a stack when the newest item should be handled first. Undo actions, browser-like history, recursive traversal rewritten as a loop, syntax validation, and temporary backtracking all fit this pattern. If you need first-in, first-out behavior, use a queue instead. If you need sorted removal, use a priority queue.

Keep stack operations small and predictable: push, pop, peek, and empty checks. If you need to process every item in order, the list iteration guide may be a better fit. If you need to reverse order once and then read it, the Python reverse list guide covers simpler options.

Use A List As A Stack

Appending and popping at the end of a list are efficient and make the list a natural stack. Keep the top at the end consistently. Do not use pop(0) for stack removal because that shifts every remaining element.

stack = []

stack.append("parse")
stack.append("validate")
print(stack.pop())
print(stack.pop())
Python Pool infographic testing empty pop, underflow, capacity, aliases, and validation
Check empty-stack behavior, underflow policy, mutation, capacity, and API semantics.

Use deque When Both Ends Matter

collections.deque is designed for fast appends and pops on both ends. It is a good choice when the same component may switch between stack-like and queue-like operations, or when its API makes the boundary clearer.

from collections import deque

stack = deque()
stack.append("first")
stack.append("second")
print(stack.pop())
print(stack)

Define Underflow And Ownership

Popping an empty list or deque raises IndexError. That is useful when empty state is a programming error, but a user-facing parser may prefer a check, a sentinel, or a custom exception. Keep the stack’s ownership and mutation policy explicit when it crosses a function boundary.

def pop_or_none(stack):
    return stack.pop() if stack else None

print(pop_or_none([]))
print(pop_or_none(["ready"]))

Python’s list-as-stack tutorial and deque documentation define the operations and performance tradeoffs.

For related frontier structures, compare breadth-first search, queue inspection, and heap-based priority work when LIFO is not the right policy.

Frequently Asked Questions

How do I implement a stack in Python?

Use a list with append() and pop() for a simple stack, or collections.deque when efficient operations at both ends are useful.

What does LIFO mean?

LIFO means last in, first out: the most recently pushed item is the first item removed.

What happens when I pop an empty stack?

list.pop() and deque.pop() raise IndexError, so check the stack or define an application-specific underflow policy first.

Is a Python list good for a stack?

Yes. Appending and popping at the end of a list are efficient and make a list a practical stack for most workloads.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted