Lists, dicts, sets, and comprehensions
Picking the right container, and writing a comprehension that reads better than the loop it replaces.
After this lesson you can
- Choose between a list, a dict, and a set for a given problem
- Write a comprehension with a filter and a transform together
- Explain why dict keys and set members must be hashable
Three containers, three different jobs. A list keeps order and allows duplicates. A set has no order and no duplicates — membership is what it is for. A dict maps a key to a value, and since Python 3.7 preserves insertion order too, though relying on that for anything other than readability is a smell.
scores = [90, 75, 90, 60] # order and duplicates both matter
seen_ids = {1, 4, 4, 9} # {1, 4, 9} — duplicates collapse
by_id = {1: "Nino", 4: "Ana"} # look up by key
x in a_set and x in a_dict (checking the keys) are both O(1) on
average. x in a_list is O(n) — it has to scan. Converting a list you
check membership on repeatedly into a set is one of the cheapest
optimisations available.
Comprehensions
A comprehension is a loop and a condition read in the order you think about them: what to keep, then where it comes from, then the filter.
nums = [1, 2, 3, 4, 5, 6]
evens_doubled = [n * 2 for n in nums if n % 2 == 0] # [4, 8, 12]
by_parity = {n: "even" if n % 2 == 0 else "odd" for n in nums}
unique_lengths = {len(w) for w in ["a", "bb", "cc", "ddd"]} # {1, 2, 3}
The list, dict, and set versions are the same shape with different
brackets. Reach for a comprehension over a hand-written loop with
.append the moment the loop's only job is building a new collection —
it is usually shorter and, once the pattern is familiar, faster to read.
Try it
def run(): students = [ {"name": "Nino", "score": 90}, {"name": "Ana", "score": 55}, {"name": "Luka", "score": 70}, ] passing = [s["name"] for s in students if s["score"] >= 60] by_name = {s["name"]: s["score"] for s in students} grades = {"pass" if s["score"] >= 60 else "fail" for s in students} return {"passing": passing, "by_name": by_name, "grades": sorted(grades)}Hashability
A dict key or a set member must be hashable, which in practice means
immutable: str, int, float, bool, and tuple (if everything
inside the tuple is itself hashable) all work. list, dict, and set
do not — Python refuses outright, because a mutable key could change
after it was hashed and silently break the lookup.
{[1, 2]: "value"} # TypeError: unhashable type: 'list'
{(1, 2): "value"} # fine — a tuple is immutable
This is the same rule interviews ask about in other languages under a
different name — Java's equals/hashCode contract exists to enforce
exactly this.
Try it yourself
2 visible tests · 2 hidden testsImplement entryPoint(words). Count how many times each word appears
(case-sensitive) and return the word with the highest count. Where two
or more words tie for the highest count, return the alphabetically
first of them. Assume the list is never empty.
entryPoint(["a","b","a","c","a"])entryPoint(["only"])
Sign up to check the hidden tests and save your progress. Sign up