Dictionary ordering, key hashing, view objects, and the iteration traps that catch experienced developers.
Dictionaries are the most used Python data structure in production code and one of the most tested in technical interviews. Most developers use them comfortably but have gaps in their understanding of how they actually work.
Insertion Order Is Guaranteed in Python 3.7
data = {}
data["c"] = 3
data["a"] = 1
data["b"] = 2
print(list(data.keys()))
print(list(data.values()))
Output:
['c', 'a', 'b']
['3', '1', '2']
Since Python 3.7, dictionaries maintain insertion order as a language guarantee. Before that, order was an implementation detail. This is worth knowing because interview questions sometimes try to catch candidates who believe dictionaries are unordered.
Mutating a Dictionary While Iterating
data = {"a": 1, "b": 2, "c": 3}
for key in data:
if data[key] == 2:
del data[key]
Output: RuntimeError: dictionary changed size during iteration
You cannot add or remove keys from a dictionary while iterating over it. The safe pattern is to iterate over a copy of the keys:
for key in list(data.keys()):
if data[key] == 2:
del data[key]
Or collect keys to delete first:
to_delete = [k for k, v in data.items() if v == 2]
for key in to_delete:
del data[key]
Dictionary Views
data = {"a": 1, "b": 2, "c": 3}
keys = data.keys()
values = data.values()
items = data.items()
print(keys)
data["d"] = 4
print(keys)
Output:
dict_keys(['a', 'b', 'c'])
dict_keys(['a', 'b', 'c', 'd'])
Dictionary views are live views of the dictionary. They update automatically when the dictionary changes. This surprises developers who expect .keys() to return a static snapshot.
The get() Method Versus Direct Access
data = {"a": 1, "b": 2}
print(data["a"])
print(data.get("a"))
print(data.get("z"))
print(data.get("z", 0))
try:
print(data["z"])
except KeyError as e:
print(f"KeyError: {e}")
Output:
1
1
None
0
KeyError: 'z'
data["z"] raises KeyError for missing keys. data.get("z") returns None. data.get("z", 0) returns the default value 0. In interview questions about handling missing keys, knowing all three patterns and when each is appropriate demonstrates practical experience.
Dictionary Comprehension Interview Problems
Problem 1:
data = {"a": 1, "b": 2, "c": 3, "d": 4}
result = {k: v for k, v in data.items() if v % 2 == 0}
print(result)
Output: {'b': 2, 'd': 4}
Problem 2:
keys = ["name", "age", "city"]
values = ["Alice", 30, "London"]
result = dict(zip(keys, values))
print(result)
Output: {'name': 'Alice', 'age': 30, 'city': 'London'}
Problem 3 — The Nested Inversion:
original = {"a": {"x": 1}, "b": {"x": 2}, "c": {"x": 3}}
result = {v["x"]: k for k, v in original.items()}
print(result)
Output: {1: 'a', 2: 'b', 3: 'c'}
The defaultdict Pattern
from collections import defaultdict
groups = defaultdict(list)
data = [("fruit", "apple"), ("vegetable", "carrot"),
("fruit", "banana"), ("vegetable", "broccoli")]
for category, item in data:
groups[category].append(item)
print(dict(groups))
Output: {'fruit': ['apple', 'banana'], 'vegetable': ['carrot', 'broccoli']}
defaultdict(list) creates a new empty list automatically when accessing a missing key. This avoids the if key not in dict pattern. Interviewers often ask candidates to implement grouping logic and the defaultdict approach signals familiarity with the standard library.
Practice dictionary problems with increasing complexity at pycodeit.com.
Top comments (0)