Python heapq: Priority Queues

Published on: August 28, 2026
Reading time: 4 minutes
Close-up of a vibrant yellow python coiled with textured scales in vibrant light.

The heapq module implements binary heaps on ordinary Python lists. A heap keeps the smallest item at index zero and supports insertion and removal in logarithmic time. This structure is useful for priority queues, shortest-path algorithms, scheduling, simulations, event processing, and selecting the largest or smallest values from a stream.

A heap is not a fully sorted list. Only the parent-child heap invariant is guaranteed. Iterating over the internal list does not produce ascending order. Use the module operations and treat the representation as an implementation detail.

Create an empty heap

A heap starts as a normal list.

import heapq

queue = []
heapq.heappush(queue, 5)
heapq.heappush(queue, 2)
heapq.heappush(queue, 8)
print(queue[0])

The smallest value is at queue[0], but the remaining positions are not fully ordered.

Remove the smallest item

heappop() removes and returns the smallest entry.

while queue:
    print(heapq.heappop(queue))

Popping an empty heap raises IndexError. Check the condition or handle the expected case.

Build a heap with heapify

heapify() rearranges an existing list in linear time.

values = [9, 1, 7, 3, 2]
heapq.heapify(values)

This is more efficient than pushing every item individually when all data is already available.

Priority queues with tuples

Tuples compare field by field. A common pattern stores priority and payload.

queue = []
heapq.heappush(queue, (10, "report"))
heapq.heappush(queue, (1, "alarm"))
priority, task = heapq.heappop(queue)

Lower numbers leave first. For highest-priority-first behavior, negate numeric priorities or use max-heap APIs available in the target Python version.

Ties and stability

If priorities tie, Python compares the second tuple field. Non-comparable tasks may raise TypeError.

from itertools import count

counter = count()
heapq.heappush(queue, (priority, next(counter), task))

The counter also preserves insertion order among equal priorities.

Keep tasks out of comparisons

Domain objects, dictionaries, and callbacks should not participate in ordering.

Use (priority, sequence, item) or a dataclass whose comparison fields are explicitly controlled.

heappushpop

heappushpop() pushes one item and removes the smallest in one combined operation.

removed = heapq.heappushpop(heap, new_item)

It is efficient for maintaining the N largest values observed so far.

heapreplace

heapreplace() removes the smallest item first and then inserts the new one.

old = heapq.heapreplace(heap, new_item)

Its result differs from heappushpop() when the new item is smaller than the current root.

Keep the largest N values

Use a fixed-size min-heap.

limit = 100
heap = []
for value in stream:
    if len(heap) < limit:
        heapq.heappush(heap, value)
    elif value > heap[0]:
        heapq.heapreplace(heap, value)

The heap contains the largest values at the end, but they are not sorted.

nsmallest and nlargest

nsmallest() and nlargest() select extreme values.

top = heapq.nlargest(10, records, key=lambda item: item.score)

For small N relative to the input, these functions can beat sorting everything. When N is close to the total size, sorted() may be simpler and faster.

Key functions

The selection helpers accept key, while heappush() does not.

For a persistent heap, include the precomputed key in each tuple and avoid recalculating it during comparisons.

Merge sorted streams

heapq.merge() combines already sorted iterables lazily.

result = heapq.merge(file_a, file_b, key=extract_key)
for item in result:
    process(item)

Every input must use the same ordering rule. The result does not load everything into memory.

Update priorities safely

The module has no direct decrease-key operation. Mutating an entry in place can break the heap invariant.

A safe strategy pushes a new entry and marks the old one as removed. Obsolete entries are skipped when popped.

REMOVED = object()
entries = {}


def add(item, priority):
    if item in entries:
        remove(item)
    entry = [priority, next(counter), item]
    entries[item] = entry
    heapq.heappush(queue, entry)


def remove(item):
    entry = entries.pop(item)
    entry[2] = REMOVED

Pop valid entries

def pop_task():
    while queue:
        priority, sequence, item = heapq.heappop(queue)
        if item is not REMOVED:
            del entries[item]
            return item
    raise KeyError("empty priority queue")

Removed entries consume memory until they reach the root. Rebuild the heap periodically when updates are frequent.

Max-heaps

Traditional Python code represents a max-heap by negating numeric priorities.

heapq.heappush(queue, (-priority, sequence, item))

Do not negate non-numeric values. Also check the dedicated max-heap APIs available in the project’s minimum Python version.

Complexity

heappush() and heappop() are O(log n), reading the root is O(1), and heapify() is O(n).

These bounds do not include expensive key comparisons or large payload handling.

Graph algorithms

Priority queues appear in Dijkstra, A*, Prim, and event simulations.

Without decrease-key, push new distances and ignore stale entries when they are popped. Validate that Dijkstra edge weights are nonnegative.

Event scheduling

A heap can store (time, sequence, callback).

heapq.heappush(events, (when, next(counter), callback))

Use a monotonic clock for durations and never run long callbacks while holding a queue lock.

Threads

heapq does not synchronize access. Multiple threads require a lock or queue.PriorityQueue.

Python concurrent.futures can coordinate workers, but queue capacity and shutdown policy still matter.

Backpressure

An unlimited priority queue can grow until memory is exhausted.

Define capacity, rejection, persistence, or low-priority eviction. Consumers may not always keep up with producers.

Mutable priorities

If priority depends on an attribute that changes after insertion, ordering becomes invalid.

Store an immutable key and update by reinserting a new entry.

Get sorted output

Repeatedly pop items to preserve the heap process, or call sorted(heap) if the original heap is no longer needed.

Direct iteration over the internal list is not sorted output.

Persistence

The internal list can be serialized, but that exposes implementation details and obsolete entries.

Persist logical tasks and priorities, then reconstruct with heapify().

Security

External priorities can starve important work or monopolize capacity. Apply authorization, quotas, and service classes.

Never execute callbacks supplied by untrusted users.

Testing

Test empty queues, ties, negative priorities, updates, cancellation, removed entries, top-k logic, high volume, and concurrency.

Compare results with sorted() in property-based tests.

Common mistakes

Common failures include assuming the list is sorted, comparing non-comparable tasks, mutating priorities in place, confusing heapreplace() with heappushpop(), forgetting stable tie-breaking, and allowing unlimited growth.

Conclusion

heapq implements efficient priority queues on lists. Use tuples with a priority and sequence number, keep comparison keys immutable, and use lazy deletion for updates.

Consult the official heapq documentation, Python concurrent.futures, and the upcoming guide to bisect for ordered lists.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Detailed shot of a yellow measuring tape with numbers on a dark background.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python array: Compact Numeric Data

    Learn Python array for compact numeric storage, typecodes, bytes, files, memoryview, portable binary layouts, and safe validation.

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026
    Detailed view of programming code in a dark theme on a computer screen.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python struct: Binary Data Formats

    Learn Python struct for binary packing, endianness, offsets, padding, buffers, sockets, versioned layouts, and safe validation.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Composition of black and white photos and old travel log placed with vintage island map and obsolete photo camera on narrow wooden table
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python mmap: Memory-Mapped Files

    Learn Python mmap for memory-mapped files, byte searches, in-place edits, shared pages, aligned offsets, and safe synchronization.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Close-up of a USB pen drive being inserted into a laptop USB port on a white surface.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python importlib.metadata: Versions and Plugins

    Learn Python importlib.metadata to query versions, requirements, files, distributions, entry points, and plugins without importing packages.

    Ler mais

    Tempo de leitura: 9 minutos
    27/08/2026
    Detailed view of programming code in a dark theme on a computer screen.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    importlib.resources: Read Packaged Files

    Learn Python importlib.resources to read templates and package data with Traversable, files, and as_file across wheels, ZIPs, and frozen apps.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python runpy: Execute Modules and Scripts

    Learn Python runpy to execute modules and scripts, control __main__, run_path, alter_sys, namespaces, testing, and process isolation.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026