Priority Queues in Python: Concepts and Practical Implementations
A priority queue is a data structure that stores elements alongside a priority value, allowing efficient retrieval of the item with the highest or lowest priority. Python provides several ways to implement priority queues depending on the use case:
- The
heapqmodule delivers a lightweight and efficient min-heap implementation with low memory overhead. - In multithreaded applications,
queue.PriorityQueueoffers a thread-safe wrapper built on top ofheapq. - Max-heaps can be implemented in two common ways:
- By reversing priorities using negative values.
- By creating a custom class that controls ordering through the
__lt__comparison method.
This guide explores each of these approaches with practical examples and real-world usage patterns.
Key Takeaways
After completing this tutorial, you will be able to:
- Explain what a priority queue is and how it differs from a standard queue.
- Create a simple priority queue with Python’s built-in
heapqmodule. - Use
queue.PriorityQueueto build thread-safe priority queues. - Construct both min-heap and max-heap versions using priority inversion.
- Design custom priority queue classes by implementing comparison behavior.
- Select the best priority queue approach for your particular use case.
- Use priority queues in real-world contexts such as scheduling, task handling, and resource distribution.
- Improve performance by processing ordered data with priority queues.
- Troubleshoot typical problems encountered with Python priority queues.
- Apply best practices for implementing and using priority queues.
Key Concepts
PriorityQueueis a thread-safe priority-queue implementation, designed for safe access and updates in multi-threaded programs.- The
putmethod inserts tasks into the queue, typically as (priority, task) where priority comes first and the task follows. - The
getmethod returns the task with the highest priority from the queue. - The task_done method signals that a retrieved task has been finished.
- The
joinmethod waits until every task in the queue has been handled and marked completed.
Prerequisites
Before you begin, confirm you have:
- Python 3.7 or later installed.
- Comfort with lists, functions, and classes.
- Optional: a basic understanding of multithreading.
What Is a Priority Queue?
A priority queue keeps items as (priority, item) pairs so that removal happens based on priority order (highest priority first, or lowest first in a min-heap). Python includes two built-in options: heapq and queue.PriorityQueue.
Priority queues are widely valuable in practical systems and can help many different user groups.
What are some use cases and applications for priority queues?
- Operating Systems: Running critical processes first through priority-based scheduling.
- Network Routers: Controlling traffic flow by favoring specific packet types.
- Healthcare Systems: Sorting emergency room patients by severity.
- Task Management Software: Ordering work items by urgency and importance.
- Game Development: Driving AI choices and timing events.
- Resource Management: Assigning limited resources to competing demands.
Who Can Use Priority Queues?
Software Developers
- Backend engineers building job queues.
- Game developers organizing event execution.
- System programmers working on schedulers.
Data Scientists
- Implementing algorithms such as Dijkstra’s shortest path.
- Ordering compute tasks by importance.
System Architects
- Planning distributed systems.
- Creating load balancers and request-handling components.
Business Applications
- Customer support ticket prioritization.
- Project management workflows.
- Inventory management processes.
Priority queues are especially helpful when you need to:
- Handle items in a defined order based on importance.
- Use limited resources effectively.
- React to real-time events that need immediate processing.
- Run algorithms that rely on ordered processing.
Realizing a priority queue using »heapq«?
The heapq module supplies a min-heap that works well as the foundation for a priority queue.
The following code sample shows a priority queue created with heapq in Python. A priority queue stores elements together with priority values so you can efficiently retrieve the element with the highest or lowest priority.
The snippet starts with an empty priority queue and inserts three tasks with different priorities. Each task is stored as a tuple where the first value is the priority and the second value is the task label.
heapq.heappush inserts tasks into the heap, while heapq.heappop removes and returns the task with the smallest priority.
import heapq
pq = []
# push
heapq.heappush(pq, (2, "code"))
heapq.heappush(pq, (1, "eat"))
heapq.heappush(pq, (3, "sleep"))
# pop – always smallest priority
priority, task = heapq.heappop(pq)
print(priority, task) # 1 eat
Output
1 eat
2 code
3 sleep
The results show that the smallest priority task (“eat” with priority 1) is returned first, followed by higher priorities (“code” with priority 2 and “sleep” with priority 3).
heapq keeps the smallest tuple at index 0, which enables fast access to the highest-priority element (in a min-heap sense). Every push and pop operation runs in O(log n) time, where n is the number of heap elements. Memory usage is O(n) because the heap holds all entries.
Benefits of Using heapq
| Benefit | Description |
|---|---|
| Efficiency | heapq keeps the smallest tuple at index 0, enabling efficient retrieval of the highest-priority element. |
| Simplicity | heapq is included with Python and needs no extra installation or setup. |
| Performance | heapq is tuned for speed and low memory overhead. |
Limitations of Using heapq
| Limitation | Description |
|---|---|
| No Maximum Priority | heapq supports a min-heap by default, so it does not directly provide a max-heap. |
| No Priority Update | heapq does not include a built-in method to change the priority of an existing element. |
Min-Heap or Max-Heap?
Min-heaps and max-heaps are tree-based structures that follow specific ordering rules.
Min-Heap
- Each node’s value is less than or equal to the values of its children
- The root node holds the minimum value in the heap
- Useful when you need fast access/removal of the smallest element
- Python’s
heapqis implemented as a min-heap
Example min-heap:
1
/ \
3 2
/ \ /
6 4 5
Max-Heap
- Each node’s value is greater than or equal to the values of its children
- The root node stores the maximum value in the heap
- Useful when you need fast access/removal of the largest element
Example max-heap:
6
/ \
4 5
/ \ /
1 3 2
Realizing Max Heap with heapq?
Because heapq is a min-heap, you can still achieve max-heap behavior using one of these approaches:
- Flip priorities by using negative values
- Create a custom class and define ordering with
__lt__
Below are both max-heap implementations with heapq.
1. How to Implement a Max-Heap using Inverting Priorities(using negative values)
You can mimic a max-heap with heapq by negating numbers before pushing them and negating again when you pop. This works because negation reverses ordering (for example, if a > b, then -a < -b), so the min-heap effectively behaves like a max-heap.
import heapq
# Initialize an empty list to act as the heap
max_heap = []
# Push elements into the simulated max-heap by negating them
heapq.heappush(max_heap, -5)
heapq.heappush(max_heap, -1)
heapq.heappush(max_heap, -8)
# Pop the largest element (which was stored as the smallest negative value)
largest_element = -heapq.heappop(max_heap)
print(f"Largest element: {largest_element}")
Ooutput
Largest element: 8
The result confirms that the largest value (8) is returned first, followed by lower values (-5 and -1).
Space complexity: O(n), where n is the number of elements, because all values are stored in the heap.
Time complexity: O(log n) per insertion and removal, since heapq.heappush and heapq.heappop each take O(log n).
Note: The overall time complexity becomes O(n log n) because there are n insertions and one extraction.
Benefits of Max-Heap using Negative Priorities
- Easy and direct to implement.
- Works smoothly for numeric data.
- No need to write a custom class.
- Keeps O(log n) operation complexity.
- Memory-friendly because it only stores negated values.
Drawbacks of Max-Heap using Negative Priorities
- Limited to numeric values.
- Very large integers may overflow.
- Double negation can reduce readability.
- You cannot view the true values in the heap unless you negate them back.
- Not a good fit for complex objects or non-numeric priorities.
2. How to Implement a Max-Heap with a Custom Class using »__lt__«?
A max-heap can also be created with a custom class that defines ordering via __lt__. This approach is more flexible and fits an object-oriented style, because it lets you control how items are compared and arranged inside the heap.
class MaxHeap:
def __init__(self):
# Initialize an empty list to act as the heap
self.heap = []
def push(self, value):
# Push elements into the simulated max-heap
heapq.heappush(self.heap, value)
def pop(self):
# Pop the largest element from the heap
return heapq.heappop(self.heap)
def __lt__(self, other):
# Compare two MaxHeap instances based on their heap contents
return self.heap < other.heap
# Example usage
# Create two MaxHeap instances
heap1 = MaxHeap()
heap2 = MaxHeap()
# Push elements into the heaps
heap1.push(5)
heap1.push(1)
heap1.push(8)
heap2.push(3)
heap2.push(2)
heap2.push(9)
# Compare the heaps
print(heap1 < heap2) # This will compare the heaps based on their contents
Output
True
The output True means heap1 is considered less than heap2 because the comparison uses the heap contents. Here, the largest value in heap1 is 8, while heap2’s largest value is 9. Since 8 is smaller than 9, heap1 is treated as “less than” heap2.
Time complexity: O(log n) for each insert and remove operation, where n is the number of stored values, because heapq.heappush and heapq.heappop run in O(log n).
Space complexity: O(n), since all values are stored in the heap.
Benefits of Max-Heap using Custom Class
- Supports non-numeric values or complex objects as priorities.
- Allows direct comparisons without using negation.
- Often feels clearer and more intuitive to read.
- Lets you apply custom comparison rules.
Drawbacks of Max-Heap using Custom Class
- Requires writing and maintaining a custom class.
- For large datasets, object creation and comparisons can add overhead.
- May be harder for beginners to implement and grasp.
- May be unnecessary when numeric priorities and simplicity are enough.
Build a priority queue using queue.PriorityQueue
The queue.PriorityQueue class is a thread-safe priority queue. It is built on top of heapq and provides a sturdier way to handle tasks with different priorities across multiple threads.
Here is a working example that uses queue.PriorityQueue:
from queue import PriorityQueue
import threading, random, time
# Create a PriorityQueue instance
pq = PriorityQueue()
# Define a worker function that will process tasks from the priority queue
def worker():
while True:
# Get the task with the highest priority from the queue
pri, job = pq.get()
# Process the task
print(f"Processing {job} (pri={pri})")
# Indicate that the task is done
pq.task_done()
# Start a daemon thread that will run the worker function
threading.Thread(target=worker, daemon=True).start()
# Add tasks to the priority queue with random priorities
for job in ["build", "test", "deploy"]:
pq.put((random.randint(1, 10), job))
# Wait for all tasks to be processed
pq.join()
Output
Processing build (pri=1)
Processing test (pri=2)
Processing deploy (pri=3)
The output shows tasks being executed according to priority order, where the smallest numeric priority is handled first. PriorityQueue makes this work by always returning the lowest priority number first, which functions as a priority-driven scheduling pattern.
heapq vs. PriorityQueue in Multithreading: A Comparison
Multithreading allows a single program to run multiple threads of execution at the same time, improving throughput and responsiveness. Because threads share memory and resources, synchronization problems can occur without safe coordination.
For Python priority queues, heapq and PriorityQueue are often compared, especially for multithreaded programs. Below is a detailed side-by-side breakdown:
| Feature | »heapq« | »PriorityQueue« |
|---|---|---|
| Implementation | heapq is not thread-safe and has no built-in protection for concurrent access and changes. | PriorityQueue is thread-safe, ensuring operations are safely performed in multi-threaded environments. |
| Data Structure | heapq uses a list underneath. | PriorityQueue uses a queue structure that is better aligned with multi-threaded usage. |
| Complexity | The time complexity of heapq insertion and removal operations is typically O(log n), where n represents the number of elements in the heap. | PriorityQueue operations also run in O(log n) time, while additionally providing thread-safe behavior for concurrent environments. |
| Usage | Best for single-threaded programs where queue operations are not concurrent. | Designed for multi-threaded programs where concurrent access and updates are required. |
| Synchronization | Manual synchronization is needed because heapq is not thread-safe. | Built-in synchronization removes the need for manual locking. |
| Blocking | No blocking operations are included, so you must implement your own waiting behavior. | Blocking operations are built in, letting threads wait for work or for completion. |
| Task Completion | Task completion must be handled explicitly by your code. | Task completion support is included, which simplifies development. |
| Priority | Priority handling must be set up manually. | Priority management is supported directly by default. |
| Performance | Often faster because it is simpler and has less overhead. | Typically slower because thread safety and synchronization add overhead. |
| Use Case | Ideal for single-threaded situations where speed matters most and concurrency is not involved. | Best for multi-threaded scenarios where safe access, synchronization, and priority handling matter. |
FAQs
1. What is a priority queue in Python?
A priority queue in Python is a structure that inserts and removes elements based on priority. Each entry has a priority value, and removal happens in priority order. In Python, priority queues are commonly created using the heapq module or the queue.PriorityQueue class.
2. How do I implement a priority queue in Python?
Two widely used approaches are:
Using heapq module:
import heapq
# Create a priority queue
pq = []
# Add elements to the priority queue
heapq.heappush(pq, (3, 'task3')) # Priority 3
heapq.heappush(pq, (1, 'task1')) # Priority 1
heapq.heappush(pq, (2, 'task2')) # Priority 2
# Remove elements from the priority queue
while pq:
priority, task = heapq.heappop(pq)
print(f"Priority: {priority}, Task: {task}")
Using queue.PriorityQueue class:
from queue import PriorityQueue
# Create a priority queue
pq = PriorityQueue()
# Add elements to the priority queue
pq.put((3, 'task3')) # Priority 3
pq.put((1, 'task1')) # Priority 1
pq.put((2, 'task2')) # Priority 2
# Remove elements from the priority queue
while not pq.empty():
priority, task = pq.get()
print(f"Priority: {priority}, Task: {task}")
3. Is Python’s heapq a min-heap or max-heap?
Python’s heapq module is a min-heap by default. That means the smallest element (based on priority) is placed at the root. As items are added or removed, the structure rearranges itself to preserve this property.
A max-heap can still be achieved by:
- Reversing priorities (using negative values).
- Creating a custom class with the
_lt_comparison method.
Both approaches were covered earlier, so refer to the relevant sections above.
4. When should I use a priority queue?
A priority queue is a strong choice when tasks or elements must be processed in a defined order based on importance. Typical examples include:
- Task scheduling: Execute tasks by urgency or importance.
- Resource allocation: Assign resources to work based on priority.
- Event handling: Process critical events before less important ones.
- Job scheduling: Order jobs to improve overall resource usage.
In general, a priority queue fits whenever elements must be handled according to priority order.
5. When to use »heapq«?
- For single-threaded programs where performance is the top concern and operations are not concurrent.
- When you can reasonably handle manual synchronization and task completion logic yourself.
6. When to use »PriorityQueue«?
- For multi-threaded programs where thread safety, synchronization, and priority handling are important.
- When you want built-in synchronization, blocking behavior, and task completion support for safe concurrency.
Conclusion
This tutorial explained how to implement a priority queue in Python with both heapq and queue.PriorityQueue. It also covered how to create a max-heap using these tools.
It also compared heapq and PriorityQueue for multithreaded usage. In short, heapq is typically favored for single-threaded performance-focused programs, while PriorityQueue is better for multi-threaded programs that require safe synchronization.
In addition, common priority-queue questions were addressed to provide a well-rounded understanding of how priority queues work in Python and how to implement them effectively.


