Python Data Structures
Theme
Palette
Download
Caption (tap to copy)
📖 Deep dive (full written explanation)
Post four is the practical workout. The cover signals intent: this one is code-heavy by design, because container fluency comes from typing the operations until they're reflexive, not from reading about them. The plan is to cover creation, slicing, and mutation for each container, then the high-value patterns — comprehensions, dict counting, set algebra, and unpacking.
The recommendation throughout is active: run each snippet, change a value, and observe the result. That feedback loop is what converts syntax into instinct.
This first snippet exercises the list operations you'll use constantly. sort() mutates in place and returns None — a common gotcha worth seeing early. Slicing with [1:4] extracts a sub-range, and the [::-1] idiom reverses by stepping backward. The comprehension at the end filters and transforms in a single expression.
Comprehensions deserve special attention: they're the idiomatic Python way to build a list from another iterable, replacing the verbose append-in-a-loop pattern with one readable line that also runs faster.
Dict counting is one of the most common real tasks in data work, and this snippet shows the canonical manual approach. The .get(w, 0) call is the key idea: it returns a default of 0 when the key is absent, so you can increment uniformly without a separate 'if key in dict' branch. That avoids KeyError and keeps the loop clean.
Iterating with .items() yields key-value pairs together, which is almost always what you want — far cleaner than iterating keys and indexing back into the dict.
This snippet shows the idiomatic upgrade: Counter from the collections module does the counting loop for you in one line, and most_common ranks entries by frequency for free. For any frequency or tally task, Counter is the tool to reach for rather than rebuilding the .get pattern.
The dict comprehension at the end mirrors the list comprehension from earlier, transforming every value while keeping keys. Comprehensions exist for all three of list, dict, and set, and using them is the mark of idiomatic Python.
Set algebra is where sets earn their keep. The four operators map directly to the math: union (|) combines, intersection (&) keeps shared items, difference (-) subtracts, and symmetric difference (^) keeps items in exactly one set. Each is a single readable operator that would otherwise be a loop with conditionals.
The final line is the dedup idiom — wrapping any iterable in set() collapses duplicates instantly. These operations are the reason set-based solutions to 'what's common,' 'what's unique,' and 'what's missing' are both fast and obvious to read.
Tuple unpacking is a small feature with outsized everyday impact. Assigning a tuple to multiple names spreads its elements out, which is how functions cleanly return several values. The swap idiom exchanges two variables with no temporary, because the right side is packed into a tuple before assignment.
Star unpacking captures a variable-length tail into a list, which is invaluable for 'first item plus the rest' patterns. These forms make code that handles structured data read like plain description rather than index juggling.
The comparison diagram contrasts two set operations side by side so the semantics stick. Intersection keeps only what's in both sets; difference keeps what's in the first but not the second. Seeing concrete results — {3, 4} versus {1, 2} for the same inputs — anchors the operators to outcomes rather than symbols.
This visual reinforces that set algebra answers relational questions ('shared,' 'only in this one') directly, which is exactly why it replaces tangled membership loops so cleanly.
The tips distill the post into the patterns worth memorizing. .get with a default avoids KeyError on counting and lookup; Counter handles any frequency task; set() dedupes in one call; comprehensions replace manual build-up loops; and unpacking cleans up multi-value returns. Each is a small habit that compounds into noticeably cleaner code.
These aren't advanced tricks — they're the baseline idioms that distinguish fluent Python from translated-from-another-language Python.
This snippet shows the containers working together, which is how real data is shaped: a list of dicts, each holding a set. The loop accumulates a union across all the sets with the |= operator, collecting every distinct tag. sorted() then turns the unordered set into a stable, ordered list for display.
Nesting is where the day's lessons combine — choosing list for the ordered collection, dict for the named records, and set for the unique tags, each because its guarantee fits that level of the structure.
The closing mistake catches a subtle trap that the clean syntax can hide: shared references inside nested structures. Building a grid with [[0]*3]*3 creates three references to the same inner list, so writing to one row writes to all three. It's a classic surprise that survives review because the code looks correct.
The fix is to use a comprehension — [[0]*3 for _ in range(3)] — which evaluates the inner list fresh on each iteration, producing genuinely independent rows. This previews the reference-versus-copy theme that the final post explores in depth.
The CTA moves from constructive patterns to defensive ones. Having built fluency with the operations, post five names the traps — the container mistakes that bite even experienced developers — so the same fluency doesn't lead you confidently into a bug.