Python

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

One pass, three different collections built from itpython-3.12
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)}
What to look for

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 tests

Implement 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"])
Loading editor…

Sign up to check the hidden tests and save your progress. Sign up