Quick answer: Python’s traditional heapq API is a min heap, so a numeric max heap can store negative priorities: push(-priority, item) and negate the priority returned by heappop(). For non-numeric priorities, use a comparable wrapper or an explicit ordering key with a tie-breaker.

A max heap keeps the largest item ready at the root. In Python, the standard heapq module is built around a min heap, which means the smallest item is stored at index 0. To build a max heap in Python code that works across common Python 3 versions, store numbers with the sign reversed. The smallest stored value then represents the largest original value.
The official Python heapq documentation is the reference for heap operations.
The negation pattern is simple: push -value into the heap, then negate the popped result to recover the original value. This works because heapq always removes the smallest stored value first. For positive scores, the largest score becomes the most negative stored value.
A heap is not a sorted list. It only guarantees that the next item is available at index 0. The rest of the list is arranged to preserve the heap rule, so printing the raw list can look surprising. Use heap operations for adding and removing items, and use sorted() only when a fully ordered display is needed.
Some newer Python documentation includes dedicated max-heap helpers. Use those only after checking the Python version your project runs in. The examples below use the portable approach because it is explicit, easy to test, and works with the long-established heapq API.
Create A Max Heap
Start with an empty list and push each number with the sign reversed. The largest original number is recovered by negating the value returned from heappop().
import heapq
max_heap = []
for number in [12, 3, 19, 7]:
heapq.heappush(max_heap, -number)
largest = -heapq.heappop(max_heap)
print(largest)
print([-item for item in max_heap])
The printed heap contents may not be sorted after the pop, but the next largest value is still represented at the root. Always treat the list as heap storage rather than as final sorted output.
Heapify Existing Numbers
If you already have a list of numbers, build the stored list with negated values and call heapify() once. This is clearer and faster than pushing many starting values one at a time.
import heapq
numbers = [5, 14, 2, 30, 9]
max_heap = [-number for number in numbers]
heapq.heapify(max_heap)
while max_heap:
print(-heapq.heappop(max_heap))
This prints values from largest to smallest. The original list remains available if the rest of the program still needs it. When memory matters, you can also overwrite the list intentionally, but keep that choice obvious in the surrounding code.

Peek At The Largest Item
Peeking should not remove anything. For a negated max heap, the largest original value is -heap[0]. Check for an empty heap before reading index zero.
import heapq
def peek_max(heap):
if not heap:
return None
return -heap[0]
max_heap = []
for score in [18, 41, 27]:
heapq.heappush(max_heap, -score)
print(peek_max(max_heap))
print(len(max_heap))
This helper returns None for an empty heap. If None could be a valid result in your program, raise an exception or return a custom sentinel instead. The important part is to make empty-heap behavior deliberate.
Build A Max Priority Queue
A priority queue often stores work items with a score. Negate the priority so the highest score is processed first, and add a counter to keep equal-priority tasks stable.
import heapq
from itertools import count
order = count()
tasks = []
for priority, task in [(5, "write docs"), (10, "fix outage"), (10, "review patch")]:
heapq.heappush(tasks, (-priority, next(order), task))
while tasks:
priority, _, task = heapq.heappop(tasks)
print(-priority, task)
The counter prevents Python from comparing task payloads when priorities tie. It also keeps equal-priority work in the order it was added. This tuple shape is a strong default for small schedulers, ranking pipelines, retry systems, and search algorithms.

Keep The Top K Values
When you only need the largest few values, a small min heap is often better than a full max heap. Keep the best k values in a normal min heap, where the smallest kept value is easy to replace.
import heapq
def top_k(values, k):
heap = []
for value in values:
if len(heap) < k:
heapq.heappush(heap, value)
elif value > heap[0]:
heapq.heapreplace(heap, value)
return sorted(heap, reverse=True)
print(top_k([8, 3, 15, 1, 22, 9, 17], 3))
This pattern avoids storing every candidate in a max heap. The heap never grows beyond k items, so it is a good fit for leaderboards, streaming metrics, and reports where only the best few results are needed.
Wrap Max Heap Operations
A small class can hide the sign reversal so application code does not repeat it at every call site. The wrapper still uses heapq internally.
import heapq
class MaxHeap:
def __init__(self):
self._items = []
def push(self, value):
heapq.heappush(self._items, -value)
def pop(self):
if not self._items:
raise IndexError("pop from empty max heap")
return -heapq.heappop(self._items)
def peek(self):
if not self._items:
raise IndexError("peek from empty max heap")
return -self._items[0]
heap = MaxHeap()
heap.push(4)
heap.push(11)
heap.push(6)
print(heap.peek())
print(heap.pop())
A wrapper is useful when max-heap behavior appears in more than one place. It keeps the public methods positive and predictable, while the private list stores negated values. Add methods such as __len__ or clear() only when the caller genuinely needs them.
Use a max heap when code repeatedly needs the current largest item after inserts. Use a simple call to max() when you only need the largest value once. Use sorted(values, reverse=True) when the whole result must be ordered. For top-k problems, use a bounded min heap so the heap stays small.
The practical takeaway is that Python’s built-in heap tools are min-heap tools first. For portable max heap Python code, negate numeric priorities, document the tuple shape for priority queues, avoid depending on the raw heap list order, and test empty heaps, duplicate priorities, negative numbers, and one-item heaps.
Understand The Heap Invariant
A heap stores the smallest root under heapq’s normal ordering, not a fully sorted list. The root is the next item to remove, while the remaining elements are only partially ordered. Use heappush() and heappop() rather than indexing arbitrary positions as though the list were sorted.

Negate Numeric Priorities
For a max-priority queue, store (-priority, item). The most positive logical priority becomes the most negative heap value and reaches the root. Keep the conversion at the adapter boundary so callers continue to work with normal positive priorities.
Handle Ties Safely
Heap entries compare tuple elements from left to right. If two priorities are equal and their payloads are not orderable, add a monotonically increasing counter as a tie-breaker: (-priority, counter, item). This also makes equal-priority behavior deterministic.

Peek, Pop, And Keep Top K
heap[0] lets you inspect the next logical maximum after applying the negation convention. heappop() removes it, while heappush() adds a new entry in logarithmic time. For top-k work, keep a bounded heap and decide whether a min-heap of the current winners is simpler than negating every value.
Test Empty, Equal, And Invalid Inputs
Test empty heaps, one item, duplicate priorities, negative and zero priorities, non-comparable payloads, and a large stream. Assert both logical output and the representation invariant, and make clear whether the caller owns the heap list or receives a copy.
The official heapq documentation defines the min-heap invariant and priority-queue patterns. Related guidance includes data-driven selection and priority-queue tests.
For related priority queues, compare heapq operations, min-heap behavior, and priority-queue design when choosing a tie-breaking contract.
Frequently Asked Questions
Does Python heapq provide a max heap?
The traditional heapq interface is a min heap, so numeric max-heap examples commonly store negated priorities.
How do I push and pop a max-heap item?
Push the negated priority with heappush() and negate the value returned by heappop() to recover the logical priority.
How do I handle equal priorities?
Store a monotonically increasing tie-breaker or a comparable secondary value so heap entries remain orderable.
What is the complexity of heap operations?
Peeking at the root is constant time, while heappush() and heappop() take logarithmic time in the number of items.