Use a list for an ordered sequence that you will change, a tuple for a fixed group of values, a set when each value should appear only once and you need fast membership tests or set arithmetic, a dict when each value is found by a unique key, and a collections.deque when you add or remove items at both ends. No container is the best choice everywhere. The right one depends on whether order matters, whether the container must change, how you look items up, whether duplicates are allowed, and where items enter and leave.
Decide with five questions
Work through these questions in order. The first “yes” usually settles the choice.
As an Amazon Associate I earn from qualifying purchases.
- Does each value need a unique key to be found? If yes, use a
dict. - Must values be unique, with fast membership checks or union, intersection, and difference operations? If yes, use a
set. - Do you add or remove items at both ends, as in a queue? If yes, use a
collections.deque. - Should the sequence stay fixed after creation? If yes, use a
tuple. - If none of the above applies, use a
list.
The table below is a reference for checking the same properties side by side.
| Container | Mutable after creation | Keeps order | Allows duplicates | How you read items | Typical use |
|---|---|---|---|---|---|
list |
Yes | Yes | Yes | Integer index, iteration | Ordered collections, stacks |
tuple |
No | Yes | Yes | Integer index, unpacking | Fixed groups of unlike values |
set |
Yes | No; do not rely on iteration order | No | Membership tests, set operations | Unique items, de-duplication |
dict |
Yes | Yes, by insertion order | Values yes; keys no | Unique hashable key | Key-value lookup |
collections.deque |
Yes | Yes | Yes | Position; fast at both ends | Queues, sliding buffers |
Container by container
List: the default ordered sequence
A list suits data that is ordered, changes over time, and is read by position or by iterating over it. Lists work well as stacks: append() pushes an item onto the end, and pop() removes and returns the last one. Adding or removing at the front is a different story. The Python tutorial notes that front insertion and removal are slow because the remaining elements must shift, so a list should not serve as a long-lived queue.
#1 Best Overall
stack = []
stack.append("first")
stack.append("second")
top = stack.pop() # "second"
Tuple: a fixed group of values
A tuple fits a record whose parts have different meanings, such as a coordinate pair or a row of database fields. Readers access items by position or unpack them into names, which makes the intent clear.
point = (3, 4)
x, y = point
Immutability applies only to the tuple’s own slots. If a tuple holds a list, the list can still change, though the tuple cannot be made to point at a different object. A tuple can be a dictionary key only when everything inside it is hashable:
Rank #2
t = ([1, 2], "label")
t[0].append(3) # works: the inner list is mutable
t[0] = [9] # TypeError: the tuple does not support item assignment
grid_key = (3, 4)
visited = {grid_key: True} # works: all parts are hashable
Set: unique items and membership
A set stores each value once and answers “is this present?” without scanning the whole collection. It also supports union (|), intersection (&), and difference (-). Sets are unordered, so code should not depend on the sequence in which items come out.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsseen = set() # an empty set
seen.add("alice")
"alice" in seen # True
The empty literal {} creates an empty dictionary, not a set, which is why set() is the correct way to start an empty one.
Dict: values found by key
A dictionary maps each unique, hashable key to a value. Use it when the question is “what is stored under this name?” rather than “what is at position 5?”. The two common lookup styles differ in how they treat missing keys:
d[key]raises aKeyErrorwhen the key is absent. Use it when a missing key indicates a bug.d.get(key, default)returns the default instead. Use it when absence is normal.
prices = {"apple": 0.50, "pear": 0.75}
prices.get("kiwi", 0.0) # 0.0
Deque: queues and work at both ends
The tutorial’s guidance on queues is direct: to implement a queue, use collections.deque, which was designed to have fast appends and pops from both ends. A deque is the right tool for first-in, first-out processing and for any workload that pushes and pops from either side.
from collections import deque
jobs = deque()
jobs.append("task-1") # enqueue at the right
jobs.append("task-2")
next_job = jobs.popleft() # dequeue from the left, "task-1"
Documented performance costs
The CPython time-complexity page and the collections documentation give the following figures. They describe documented CPython behavior and do not guarantee identical performance on every Python implementation.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →| Operation | Container | Documented cost | Qualification |
|---|---|---|---|
Retrieve by index, l[k] |
list | O(1) | CPython time-complexity table |
Append, l.append(x) |
list | O(1) | CPython table, under its usual implementation and allocation assumptions |
Membership, x in l |
list | O(n) | Scans elements until a match |
| Key membership and retrieval | dict | Average O(1) | Assumes well-distributed hashes; worst case is O(n) if all keys collide |
| Append or pop at either end | deque | Approximately O(1) | collections documentation |
The complexity page states: “This page documents the time complexity of various operations on built-in types in CPython. Other Python implementations may have different performance characteristics.” Treat these figures as a guide to which operations scale well, not as a timing promise. If speed decides between two containers, measure with your own data.
Best Value
Symptoms and fixes
TypeError: unhashable type: 'list'— a list was placed in a set or used as a dict key. Convert it to a tuple if its contents are hashable, or choose a different key design.KeyErrorin production code — the lookup should probably used.get(key, default), or the code should check membership first.- A queue slows down as it grows — the list is probably using
pop(0)orinsert(0, x). Switch to a deque and usepopleft()orappendleft(). - Set output appears in a different order each time you change the data — set iteration order is not part of the contract. Sort the items explicitly if the output must be stable.
Version and scope
The behavior of lists, tuples, sets, dictionaries, and deques described here is long-standing. Dictionary insertion order is documented as current behavior. The complexity figures came from a development-version edition of the CPython page labelled Python 3.16, and the tutorial material is from the Python 3.14 documentation. Check the documentation for the exact interpreter version you deploy, because specific guarantees can change between releases.
This guidance is drawn from Python’s official documentation. It is not based on benchmarks, so the performance statements explain what the documented costs imply rather than how fast a particular program will run.
Quick Recap
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.




