When should you use a list, tuple, set, or dict?
Quick Answer
List: ordered, mutable, allows duplicates. A general-purpose sequence. Tuple: ordered, immutable. Fixed-size records, safe as a dict key/set element. Set: unordered, unique elements, O(1) membership testing. Good for deduplication and fast "contains" checks. Dict: key→value mapping, O(1) lookup by key, insertion-ordered. The default choice whenever you need to look things up by a name/id rather than by position.
Detailed Answer
Quick decision table
| Need | Structure | Why |
|---|---|---|
| Ordered, mutable collection, duplicates OK | list | general-purpose sequence |
Fixed-size, immutable record ((x, y), (name, age)) | tuple | safe to hash, signals "this won't change" |
| Fast membership testing, deduplication | set | O(1) average in, automatic uniqueness |
| Lookup by key/name | dict | O(1) average lookup by key, not position |
Concrete examples
# list -- ordered sequence of items, order and duplicates matter
scores = [85, 92, 85, 78]
# tuple -- an immutable, fixed-shape record
point = (3, 4)
person = ("Ada", 36)
# set -- membership and uniqueness, order doesn't matter
seen_ids = {101, 205, 310}
if user_id in seen_ids: # O(1) average -- much faster than `in a_list` for large data
...
unique_tags = set(all_tags) # dedupe in one line
# dict -- look things up by key
users_by_id = {101: "Ada", 205: "Grace"}
users_by_id[101] # O(1) average
Why x in set beats x in list at scale
big_list = list(range(1_000_000))
big_set = set(big_list)
999_999 in big_list # O(n) -- scans up to a million elements
999_999 in big_set # O(1) average -- direct hash lookup
For any workload doing repeated membership checks against a large
collection, converting to a set (or using a dict if you also need
associated values) is one of the cheapest, highest-impact optimizations
available.
Tuple vs list: signaling intent, not just performance
def get_coordinates():
return (self.x, self.y) # a tuple signals "this is a fixed 2-item record"
Beyond being hashable (usable as dict keys/set elements) and slightly more
memory-efficient, using a tuple for a fixed-shape value communicates to
readers that the shape, not just the values, is meant to be fixed.
Nobody should expect to .append() to it.
Reach for list for ordered, mutable
sequences; tuple for fixed-shape, immutable records (and anything you
need to hash); set for uniqueness/fast membership testing; dict
whenever you look things up by key rather than by position. The
performance difference between O(n) list scans and O(1) set/dict lookups
is often the single biggest algorithmic win available in everyday code.