Python Stacks and Queues

Implement stacks with lists and queues with deque, handle empty structures and choose queue.Queue for work shared between threads.

Lesson 84 of 107Advanced12 min read

A stack and a queue are collections defined by the order items come out:

  • Stack: last in, first out (LIFO), like a pile of plates. Use a Python list: append() to push, pop() to remove the most recent.
  • Queue: first in, first out (FIFO), like a line at a counter. Use collections.deque: append() to join, popleft() to serve the earliest.
Python
from collections import deque
undo = ["add label", "move label"]
print(undo.pop())                 # move label: most recent first
visitors = deque(["Nora", "Idris"])
print(visitors.popleft())         # Nora: earliest first

This lesson explains each structure, why list.pop(0) is the wrong way to build a queue, how to handle removing from an empty structure, and when you need the thread-safe queue module instead. The examples come from a museum's label-editing app and visitor desk.

Stack order

A stack only adds and removes at one end, the top. That makes it the natural structure for "undo the most recent thing" and for anything nested:

Python
history = []
history.append("add label")       # push
history.append("move label")
history.append("recolour label")
print(history[-1])                # peek: recolour label
print(history.pop())              # pop: recolour label
print(history)                    # ['add label', 'move label']

A Python list is an efficient stack because append() and pop() at the end are O(1) (amortised). history[-1] peeks at the top without removing it.

Stacks appear wherever the most recent item must be handled first: undo and redo histories, the browser back button, matching brackets in an expression, depth-first search, and the call stack that Python itself uses to track function calls. Removing from the front instead would turn it into a queue, with a different order and, for a list, a much higher cost.

Queue order

A queue adds at the back and removes from the front, so items leave in arrival order. collections.deque (double-ended queue) does both in O(1):

Python
from collections import deque
desk = deque()
desk.append("Nora")               # join the back
desk.append("Idris")
desk.append("Mei")
print(desk.popleft())             # Nora
print(desk[0])                    # peek at the next: Idris
print(len(desk))                  # 2

A list can technically act as a queue with pop(0), but that's O(n): removing the first element shifts every remaining element left. With thousands of items, the difference is dramatic. deque is built as linked blocks, so both ends are cheap.

deque also supports appendleft() and pop() for the other ends, rotate(), and maxlen= to keep only the newest N items, handy for "last 10 visitors" or a rolling window of readings. Indexing into the middle of a deque is O(n), so use it for its ends, not as a general list.

Empty structures

Removing from an empty list or deque raises IndexError, and in a real program that's often a normal situation, such as an undo button pressed with nothing to undo. Decide on a policy and implement it explicitly:

Python
history = []
if history:
    last = history.pop()
else:
    last = None                    # nothing to undo

Or catch the exception where emptiness is unusual:

Python
try:
    next_visitor = desk.popleft()
except IndexError:
    next_visitor = None

Wrapping the structure in a small class with methods such as undo() and is_empty() keeps the policy in one place, so callers don't each have to remember it. Unexpected IndexError: pop from an empty deque in a traceback usually means two parts of the program disagree about whether something should be there.

Concurrency

list and deque are designed for use within one thread of a program. When several threads share work, such as one thread receiving ticket requests and others processing them, use the queue module:

Python
import queue
requests = queue.Queue()
requests.put("ticket for Nora")       # producer
item = requests.get()                 # consumer: waits if empty
requests.task_done()

queue.Queue handles the locking for you, and get() can block, waiting until an item arrives, or time out with get(timeout=5). queue.LifoQueue is a thread-safe stack and queue.PriorityQueue serves the smallest item first. For asyncio programs, asyncio.Queue plays the same role.

A safe collection is only part of a concurrent design. You also need to decide how workers stop, what happens to unfinished items on shutdown, and how errors are reported. Distributed task queues, such as Celery with Redis or RabbitMQ, extend the same idea across processes and machines.

Independent practice and a worked check

Task: build independent undo and request structures, documenting empty behaviour and operation costs.

Python
from collections import deque

undo = []                 # stack: append/pop at the end, O(1)
requests = deque()        # queue: append/popleft, O(1)

undo.extend(["crop photo", "add caption"])
requests.extend(["Nora", "Idris"])

print(undo.pop(), "|", requests.popleft())          # add caption | Nora
print(undo.pop() if undo else "nothing to undo")    # crop photo
print(undo.pop() if undo else "nothing to undo")    # nothing to undo
print(requests.popleft() if requests else "desk empty")   # Idris

Document the policy beside the code: empty structures return a message instead of raising. For the worked check, a queue follows first in, first out order.

Reference and next practice

The documentation covers using lists as stacks and queues, collections.deque and the queue module. Continue with linked lists and graph traversal. Practise with the Python exercises and Python quiz.

Stacks and Queues Summary

  • Stack order: A stack removes the most recently added item first.
  • Queue order: A queue normally removes the earliest added item first.
  • Empty structures: Removal from an empty stack or deque needs an explicit policy.
  • Concurrency: queue.Queue provides coordination features for threaded producers and consumers.

Stacks and Queues Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Stacks and Queues in a small program

Python
from collections import deque
undo = ["add label", "move label"]
print(undo.pop())
visitors = deque(["Nora", "Idris"])
print(visitors.popleft())
Output
move label
Nora
Line-by-line explanation
  1. Lines 2 to 3A list used as a stack: pop() removes the most recently added action.
  2. Lines 4 to 5A deque used as a queue: popleft() serves the visitor who arrived first.
Example 2

Undo, redo and a visitor desk

Python
from collections import deque

class Editor:
    def __init__(self):
        self.undo_stack, self.redo_stack = [], []

    def do(self, action):
        self.undo_stack.append(action)
        self.redo_stack.clear()

    def undo(self):
        if not self.undo_stack:
            return None
        action = self.undo_stack.pop()
        self.redo_stack.append(action)
        return action

    def redo(self):
        if not self.redo_stack:
            return None
        action = self.redo_stack.pop()
        self.undo_stack.append(action)
        return action

ed = Editor()
for step in ["add label", "move label", "recolour label"]:
    ed.do(step)
print(ed.undo(), "|", ed.undo(), "|", ed.redo())
print(ed.undo_stack)

desk = deque(maxlen=3)
for name in ["Nora", "Idris", "Mei", "Tomas"]:
    desk.append(name)
print(list(desk), desk.popleft(), list(desk))
print(Editor().undo())
Output
recolour label | move label | move label
['add label', 'move label']
['Idris', 'Mei', 'Tomas'] Idris ['Mei', 'Tomas']
None
Line-by-line explanation
  1. Line 9Any new action clears the redo history, as in real editors.
  2. Lines 11 to 13An empty undo stack returns None instead of raising.
  3. Lines 31 to 33maxlen=3 drops the oldest visitor automatically when a fourth arrives.

Common Stacks and Queues Mistakes

Using list.pop(0) for a queue

Don't
line = list(range(100_000))
while line:
    line.pop(0)          # O(n) each time: shifts every remaining item
Do
from collections import deque
line = deque(range(100_000))
while line:
    line.popleft()       # O(1)

Popping without handling an empty stack

Don't
def undo(history):
    return history.pop()     # IndexError when there's nothing to undo
Do
def undo(history):
    return history.pop() if history else None

Stacks and Queues in Real Projects

Stacks and queues in practice

Stacks power undo systems, expression parsers, syntax checkers that match brackets and tags, and the call stack behind every function call and traceback. Queues are everywhere work arrives faster than it can be processed: print jobs, web requests waiting for a worker, messages between services, and breadth-first search.

In Python applications, deque handles in-memory queues and rolling windows, queue.Queue coordinates threads, asyncio.Queue coordinates coroutines, and message brokers such as RabbitMQ, Redis or cloud queues connect separate programs.

Stacks and Queues FAQ

How do I implement a stack in Python?

Use a list: append() to push, pop() to remove the last item and [-1] to peek. Both operations are O(1) at the end of a list. Check if stack: before popping when it might be empty.

How do I implement a queue in Python?

Use collections.deque: append() adds to the back and popleft() removes from the front, both in O(1). For queues shared between threads, use queue.Queue instead.

Why not use a list as a queue?

Removing from the front with list.pop(0) shifts every remaining element, so it's O(n) per removal. For large queues this becomes very slow, while deque.popleft() stays O(1).

What is the difference between deque and queue.Queue?

deque is a fast general-purpose double-ended container for use within one thread. queue.Queue is a thread-safe FIFO with blocking get() and put(), designed for passing work between threads.

What is a priority queue in Python?

A queue that serves the smallest (highest-priority) item first, regardless of arrival order. Use the heapq module on a list, or queue.PriorityQueue for threads. Store (priority, item) tuples so items sort by priority.

Stacks and Queues Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choiceeasy

Which order describes a queue?

Multiple choicemedium

Which review concern is valid for stacks and queues?

Stacks and Queues Quiz

2 questions to check what you learned.

  1. Which order describes a queue?

    • Last in, first out
    • First in, first out
    • Python ignores the distinction
  2. Which statement correctly describes empty structures?

    • Removal from an empty stack or deque needs an explicit policy.
    • Only the example output matters, not the rule
    • Every operation has the same behavior