3.3.9 · D5Hashing
Question bank — Python dict and set internals
Before the traps, here is the one picture every answer leans on, plus the symbols you need.
True or false — justify
A dict lookup is always O(1).
False. It is O(1) on average when hashes spread out, but worst case is O(n) when many keys share the same low bits (or an adversarial hash) so every insert probes a long chain.
CPython dict uses separate chaining (linked lists) for collisions.
False. CPython uses open addressing: colliding entries live inside the same array (the row of boxes above), found by probing other slots, which is cache-friendly.
hash(1) == hash(1.0) == hash(True) in CPython.
True. The equality contract "==if
a == b then hash(a) == hash(b)==" spans types: 1 (int), 1.0 (float) and True (bool) all compare == and are numerically 1, so CPython deliberately makes their hashes all 1.Deleting a key sets its slot back to EMPTY.
False. It becomes a ==
DUMMY== marker; setting it to EMPTY would break probe chains that pass through that box, making later keys report "not found".A set guarantees the same iteration order as insertion.
False. Only
dict (since 3.7) preserves insertion order, via a separate entry list — not by the hash slots. set gives no order guarantee.The load factor is computed from active entries only.
False. By the definition above, where
fill = active entries plus DUMMY slots, so a table crowded with dummies can resize even with few live keys.The table size in CPython is always a power of two.
True. That is exactly why works as the index: with the mask keeps the lowest bits, equalling but faster.
Resizing changes where existing keys live.
True. The index depends on ; when changes the low--bit mask changes, so every key must be rehashed into a new slot.
You can use a tuple of integers as a dict key.
True. A tuple of immutable items is itself hashable, so its hash never changes after insertion — unlike a
list, which is unhashable.Two different keys can share the same starting slot .
True. Any two keys with the same low bits collide at even with different full hashes; the perturbed probe then scatters them apart.
Amortized O(1) insert means every individual insert is O(1).
False. A resize is a genuine O(n) event; "amortized" means its cost is spread over the ~ inserts that preceded it, averaging to O(1).
Spot the error
"Since 1 == 1.0, storing both keys gives a dict of length 2."
Error: they are the same key, so length stays 1 and the value is last-write-wins; only the first key object is kept.
"I'll use hash(key) % m as the index; power-of-two size is just an optimization."
Half-error:
h % m is correct, but CPython relies on power-of-two precisely so it can replace % with the faster h & (m-1); the two agree only when ."Linear probing is what CPython uses."
Error: linear probing causes clustering; CPython uses the perturbed probe that folds in the high hash bits (via
perturb) to break clusters."At with 5 active keys, , so it resizes."
Error: , so
fill has not crossed ; resize needs fill to reach 6."A DUMMY slot is treated as empty during search, so probing stops there."
Error: during search a DUMMY is treated as occupied — probing skips over it; only during insertion may a DUMMY be reused.
"Making a key mutable is fine as long as I don't change it."
Error: the language forbids it at the type level — mutable objects like
list raise TypeError on hash(), so you can never insert one in the first place."The high bits of the hash never affect which slot a key lands in."
Error: they enter through
perturb on later probes, guaranteeing the sequence visits every slot so no key is ever lost.Why questions
Why must probing skip dummies instead of reusing them for search?
Because a key inserted after the deleted one may sit further down the same probe chain; if a search stopped at the dummy it would wrongly report "not found".
Why does counting dummies in fill prevent slow lookups?
Dummies lengthen probe chains without holding live data, so counting them in forces an earlier cleansing resize that purges every dummy.
Why does the perturbed probe use and shift perturb by 5 bits each step?
The multiplier-plus-shift mixes the high bits of (carried in
perturb) into the index gradually, scattering colliding keys and guaranteeing the sequence eventually covers all slots.Why is for an unsuccessful search?
Each probed slot is occupied with probability , so needing probes needs the first to be full, probability ; the expected count is .
Why does that sum equal ?
Because is a geometric series with ratio ; multiplying by telescopes to , so .
Why cap at specifically?
At the expected probe count is — a small constant that keeps lookups fast; letting makes blow up.
Why does hashing beat a balanced tree for plain membership tests?
A tree is O(log n) and needs an ordering, while a hash jumps (almost) straight to the slot in O(1) average using only equality and a hash.
Edge cases
If every key hashes to the same value, what is lookup complexity?
O(n): all keys pile into one probe chain, so the "constant number of probes" guarantee collapses into a linear scan.
What happens on the very first insert into an empty table?
Slot is
EMPTY, so no probing occurs and the entry lands immediately in one step.Can a table with only DUMMY and EMPTY slots (all keys deleted) still trigger a resize?
Yes — if the accumulated dummies pushed
fill past earlier, a resize fires and rebuilds a clean table with the dummies purged.What does an unsuccessful search return, and what stops it?
It stops at the first
EMPTY slot in the probe sequence and reports "not present" — a DUMMY alone can never terminate a search.What if two keys are equal (==) but you rely on them being distinct entries?
They are stored as one entry; the table keys on equality plus equal hash, so equal keys always overwrite rather than coexist.
Is frozenset usable as a key but set is not?
Correct —
frozenset is immutable and hashable, while set is mutable and unhashable, so only the frozen version can be a key.Does CPython's dict preserve order because slots are visited in order?
No — order comes from a separate compact entry array iterated in insertion order; the hash slots themselves scatter keys unpredictably.
Recall One-line self-test
If you can explain why a DUMMY differs from EMPTY and why fill counts dummies, you have
the two ideas that most traps on this page hinge on.