Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Which data structure should you use in Python? Start with a list for an ordered, changeable collection, a dict for lookup by key, a set for unique membership, a deque for queues or both-end operations, and heapq when the next item is chosen by priority. Use a tuple for a fixed record, frozenset for an immutable set, and array.array for homogeneous numeric values. A stack and a queue are access patterns implemented with those containers, not additional built-in container classes.

Python does not define an official list of exactly ten structures. The ten practical choices below combine built-in containers, standard-library types and two common access patterns. Examples run on current Python 3; the max-heap API mentioned later requires Python 3.14.

Quick comparison

Choice Best for Mutable? Duplicates Typical access
list General ordered collections and stacks Yes Allowed Index or end
tuple Fixed records and unpacking No (top level) Allowed Index
dict Key-to-value lookup Yes Keys unique; values may repeat Hashable key
set Unique membership and set algebra Yes Not allowed Membership
frozenset Hashable, immutable sets No Not allowed Membership
array.array Typed numeric sequences Yes Allowed Index
deque FIFO queues and both-end work Yes Allowed Either end
Stack pattern LIFO workflows Depends on container Depends on container Last in, first out
Queue pattern FIFO workflows Depends on container Depends on container First in, first out
heapq Repeatedly selecting the smallest priority Heap list is mutable Allowed heap[0] then pop

Choose based on the operation you perform most, not on the apparent shape of the data. Hash-based containers require hashable keys or elements; a mutable list, for example, cannot be a dictionary key.

1. List: the flexible ordered default

A list preserves order, allows duplicates and can be changed in place. It is the usual starting point when you need indexing, iteration, appending or removing items.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
scores = [91, 84, 97]
scores.append(88)
scores[1] = 86
print(scores)       # [91, 86, 97, 88]
print(scores[0])    # 91

Appending and popping at the right end are natural list operations. However, inserting or removing at index zero shifts the remaining elements, incurring O(n) movement. Do not use a list for a busy FIFO queue; use deque instead.

2. Tuple: an immutable sequence

A tuple is an immutable sequence, useful for a fixed record such as a coordinate or a function result containing different types.

point = (3, 5)
x, y = point
print(x, y)

one = (3,)       # the comma creates a one-item tuple
not_a_tuple = (3)  # this is just the integer 3

Immutability applies to the tuple’s references, not necessarily to objects inside it:

record = ("job-7", ["queued"])
record[1].append("started")  # the nested list can change

A tuple is hashable only when all of its contents are hashable. A suitable tuple can therefore be a dictionary key or a set member; a tuple containing a list cannot.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

3. Dictionary: map meaningful keys to values

A dict is a mutable mapping with unique, hashable keys. Use it when the question is “what value belongs to this name or identifier?” rather than “what is at position 4?” Insertion-order iteration is documented.

prices = {"tea": 3.5, "coffee": 4.0}
prices["tea"] = 3.75
print(prices.get("juice", 0))  # 0

for name, price in prices.items():
    print(name, price)

Indexing a missing key raises KeyError. Use get() for a default, or test with in. Keys must be hashable, so a list cannot be used as one.

4. Set: unique values and set algebra

A set is mutable, unordered and contains no duplicate elements. It is useful for deduplication and fast membership tests, plus union, intersection and difference operations.

unique_tags = set(["python", "data", "python"])
print(unique_tags)
print("data" in unique_tags)

backend = {"api", "python", "sql"}
frontend = {"python", "javascript"}
print(backend & frontend)  # intersection
print(backend | frontend)  # union
print(backend - frontend)  # difference

Use set() for an empty set; {} creates an empty dictionary. Never rely on a stable set iteration order.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

5. Frozenset: an immutable set

frozenset has set membership and algebra operations but cannot be changed. Because it is immutable and hashable when its elements are hashable, it can itself be a dictionary key or a member of another set.

permissions = frozenset({"read", "write"})
roles = {permissions: "editor"}
print(roles[permissions])

Choose it when the collection of members is part of a fixed value, such as a permission combination, rather than a work set that will be updated.

6. Array: compact, type-constrained values

The standard-library array.array stores values constrained by a type code instead of arbitrary mixed Python objects. It is a practical option for homogeneous numeric data when that representation fits your application; do not assume it is automatically faster or smaller for every workload.

from array import array

readings = array("i", [4, 8, 12])
readings.append(16)
print(readings.tolist())  # [4, 8, 12, 16]

The type code must match values you append. For tabular analysis or advanced numerical operations, specialized third-party libraries may be more appropriate, but array is available without an extra dependency.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

7. Deque: efficient operations at both ends

collections.deque (double-ended queue) supports appending and popping from either end with approximately O(1) performance. The Python documentation recommends it for queues because list operations at the front move the remaining entries.

from collections import deque

tasks = deque(["a", "b"])
tasks.append("c")
first = tasks.popleft()
tasks.appendleft("urgent")
last = tasks.pop()
print(first, last, tasks)

Deque indexing is fast at the ends and slows toward the middle, so use a list for frequent random access. A bounded deque discards entries from the opposite end when full:

recent = deque(maxlen=3)
for item in [1, 2, 3, 4]:
    recent.append(item)
print(recent)  # deque([2, 3, 4], maxlen=3)

8. Stack: a last-in, first-out pattern

A stack describes behavior, not a separate standard built-in type. A list is usually sufficient: append the newest item and pop from the same end.

stack = []
stack.append("page A")
stack.append("page B")
current = stack.pop()
print(current)  # page B

This pattern suits undo histories, depth-first traversal and nested processing. Avoid mixing front removals into a list-based stack; it defeats the efficient end operation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

9. Queue: a first-in, first-out pattern

A queue removes entries in arrival order. Implement it with deque, not repeated list.pop(0). The Python Software Foundation’s tutorial states: “To implement a queue, use collections.deque which was designed to have fast appends and pops from both ends.”

from collections import deque

queue = deque(["first", "second"])
queue.append("third")
next_item = queue.popleft()
print(next_item)  # first

The queue is the FIFO rule; deque is the concrete container. For thread-safe producer/consumer coordination with blocking operations, consider the standard-library queue module rather than treating a bare deque as a synchronization primitive.

10. Heap-based priority queue with heapq

Use heapq when the next item should be selected by priority instead of arrival time. It maintains a heap over an ordinary list; it does not keep the entire list sorted. The invariant guarantees that the smallest item is at index zero.

import heapq

jobs = [5, 1, 3]
heapq.heapify(jobs)       # linear-time transformation
while jobs:
    priority = heapq.heappop(jobs)
    print(priority)       # 1, then 3, then 5

For records, store a priority first:

jobs = []
heapq.heappush(jobs, (2, "send email"))
heapq.heappush(jobs, (1, "restart service"))
print(heapq.heappop(jobs))  # (1, 'restart service')

Python 3.14 adds documented max-heap functions such as heapify_max and heappop_max. On earlier versions, a common approach is to negate numeric priorities or store a comparable wrapper.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How to choose among them

Choose by ordering requirement

  • Need sequence position and random indexing: list, tuple or array.
  • Need lookup by a meaningful key: dict.
  • Need unique membership or set operations: set or frozenset.
  • Need both-end operations or FIFO: deque.
  • Need the next smallest priority: heapq.

Choose by mutability and duplicates

Lists, dictionaries, sets, arrays and deques can change. Tuples and frozensets cannot change at the top level. Lists, tuples, arrays and deques preserve duplicates; sets reject them, while dictionary keys are unique and values may repeat.

Remember the cost traps

  • Repeated front insertion or removal on a list is O(n); use a deque.
  • Deque end operations are approximately O(1), but middle indexing is not its strength.
  • heapq.heapify() transforms an existing list in linear time; each pop then returns the current minimum.
  • Hash-based containers require hashable keys or elements.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting common mistakes

“My empty set became a dictionary”

Use set(), not {}. Curly braces with no entries create a dictionary.

“The queue is slow”

If the code calls pop(0) on a list, replace the container with deque and call popleft().

“A tuple changed unexpectedly”

Inspect nested objects. The tuple reference is fixed, but a list or dictionary stored inside it remains mutable.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

“Dictionary or set insertion fails”

The key or element is unhashable. Convert a mutable list to a tuple when its contents are suitable, or use a different representation.

“The heap is not sorted when printed”

That is expected. A heap only guarantees the smallest item at index zero. Repeatedly call heappop() to consume values in priority order.

Further reading

The official Python data-structures tutorial, data-type index, collections documentation and heapq documentation provide version-specific details. For a broader textbook treatment, Wiley lists Data Structures and Algorithms in Python by Michael T. Goodrich, Roberto Tamassia and Michael H. Goldwasser as a 768-page first-edition hardcover (ISBN 978-1-118-29027-9) at Wiley.

Or skip the browser setup

If your Python project also needs website screenshots for documentation or tests, ScreenshotNeo provides a single HTTP call instead of maintaining browser automation. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

See the ScreenshotNeo API documentation for all options. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Frequently Asked Questions

Are stack and queue separate Python data types?

No. They describe LIFO and FIFO access rules. A list commonly implements a stack, while collections.deque commonly implements a queue.

Can a tuple be a dictionary key?

Yes, provided every object inside the tuple is hashable. A tuple containing a list is not hashable.

Does heapq sort a list?

No. It maintains a heap invariant with the smallest item at index zero; use repeated heappop calls for priority order.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.