3.3.9 · D3Hashing

Worked examples — Python dict and set internals

4,159 words19 min readBack to topic

This is the "hit every cell" drill for Python dict and set internals. We are not learning new theory here — we are pushing the machinery through every kind of input it can meet: normal inserts, collisions, deletions, resizes, degenerate keys, and the exam-style traps.

If any symbol below feels unfamiliar, that means the parent note built it — go back and rebuild it there first. Here we use the tools.


Symbols we will lean on (built here, on this page)


The scenario matrix

Every problem this topic throws at you falls into one of these cells. The worked examples afterwards are each labelled with the cell they cover.

# Cell class What makes it special Covered by
A Clean insert, no collision first-try slot is EMPTY Ex 1
B Collision + perturbed probe two keys share low bits Ex 2
C Delete → DUMMY → probe survives chain must not break Ex 3
D Reuse a DUMMY on insert insert lands on a deleted slot Ex 4
E Resize boundary (the 2/3 line) count fill = active + dummy Ex 5
F Degenerate keys1 == 1.0 == True equal keys collapse Ex 6
G Unhashable / zero-degenerate input hash([]), empty dict Ex 7
H Limiting behaviour probe count blows up Ex 8
I Real-world word problem word-frequency counter Ex 9
J Exam twist — worst-case adversary all keys same slot Ex 10
K set-specific behaviour no values, no order guarantee Ex 11

Ex 1 — Clean insert (Cell A)


Ex 2 — Collision and the perturbed probe (Cell B)

The figure below is the state after both inserts: two keys aimed at slot 3, and the amber arrow traces b's perturbed path escaping to slot 0. Read it as the answer to this example — the text just narrates the arrow.

Figure — Python dict and set internals
Figure s01 — Ex 2 (Cell B). An 8-slot blueprint table. Key a (cyan filled) sits at its start slot 3. Key b (cyan filled) also hashes to start slot 3 but collides, so the amber curved arrow arcs from slot 3 over the busy cells to land in EMPTY slot 0 — the visual proof that the perturbed probe carries a colliding key to a fresh slot without losing it.


Ex 3 — Delete makes a DUMMY; the chain survives (Cell C)

The figure shows the danger: a is gone but b still lives further down the chain, so the deleted slot must stay "walk-through," not "stop."

Figure — Python dict and set internals
Figure s02 — Ex 3 (Cell C). Same 8-slot table after a at slot 3 is deleted: slot 3 is now amber and labelled DUMMY (a tombstone), while b still lives at slot 0. The cyan arc shows a lookup for b starting at slot 3, refusing to stop on the DUMMY, and probing onward to reach b — the key intuition being that a naive EMPTY here would have cut the chain and made b "disappear."


Ex 4 — Reusing a DUMMY on insert (Cell D)


Ex 5 — The resize boundary (Cell E)

The bar chart makes the 2/3 line literal: two bars stay under it, the third pokes over and triggers the resize.

Figure — Python dict and set internals
Figure s03 — Ex 5 (Cell E). Three cyan/amber bars show fill = 4, 5, 6 for an table, plotted against the amber dashed resize line at . The fill=4 and fill=5 bars stay under the line (cyan, "no resize"); the fill=6 bar pokes above it (amber, "RESIZE"). The intuition: the trigger is a strict crossing of the line, and fill counts dummies too.


Ex 6 — Degenerate equal keys collapse (Cell F)


Ex 7 — Unhashable and zero/degenerate inputs (Cell G)


Ex 8 — Limiting behaviour as (Cell H)

The curve shows the whole story: flat and cheap up to the CPython cap, then a wall as .

Figure — Python dict and set internals
Figure s04 — Ex 8 (Cell H). The cyan curve plots expected probes against load factor from 0 to nearly 1. Amber dots mark , , and ; the dotted white vertical line is the CPython cap. The intuition the picture carries: cost stays flat and small left of the cap, then rises like a wall toward the right — which is exactly why resizing must keep bounded.


Ex 9 — Real-world word problem (Cell I)


Ex 10 — Exam twist: the adversary (Cell J)


Ex 11 — set-specific behaviour (Cell K)


Recall Self-test — one line each

Load factor definition? ::: , where fill = active entries + dummies. Slot for hash=27, m=8? ::: . Why does the probe reach every slot? ::: it's a full-cycle permutation mod , so it visits all slots before repeating. b with hash=11 in after colliding at 3 lands where (Ex 2)? ::: slot 0. Why delete → DUMMY not EMPTY? ::: so probe chains passing through it aren't cut, losing later keys. Resize line for ? ::: fill , i.e. fires at fill = 6. Does fill = 5 resize? ::: No — . Where does come from? ::: geometric sum of "keep probing while slots are full." probes at ? ::: . Successful-search probes at ? ::: , cheaper than a miss. Ex9 rehash sum ? ::: , and because sizes double. Adversary with all-same low bits ⇒ lookup cost? ::: O(n) worst case. Does a set preserve insertion order? ::: No — only dict does (since 3.7).