Chapter 3 · Junior Developer

Dictionaries

A dictionary maps keys to values: {"alice": 90, "bob": 70}. Think of it as a labelled lookup — you fetch a value by its key, not by position. It's how Python holds structured data in memory, and it mirrors a JSON object exactly.

Look up with d[key]. If the key is missing, d[key] raises KeyError. d.get(key) is the safe alternative: it returns None (or a default you supply, d.get(key, 0)) instead of crashing.

Add or update with d[key] = value — same syntax for both: a new key is inserted, an existing key is overwritten. key in d tests whether a key is present (not a value).

To loop, three views:

  • d.keys() — the keys.
  • d.values() — the values.
  • d.items()(key, value) pairs, perfect to unpack: for name, score in d.items():.

As of Python 3.7+, dicts keep insertion order — iterating gives keys back in the order you added them.

Syntax

profiles = {"alice": 90, "bob": 70}

profiles["alice"]          # 90
profiles.get("nobody")     # None  (no crash)
profiles.get("nobody", 0)  # 0     (custom default)

profiles["carol"] = 88     # add
profiles["alice"] = 95     # update
"bob" in profiles          # True  (checks KEYS)

for name, score in profiles.items():
    ...

Worked examples

.get() avoids KeyError; supply a default

profiles = {"alice": 90, "bob": 70}
print(profiles["alice"])          # 90
print(profiles.get("charlie"))    # None
print(profiles.get("charlie", 0)) # 0
# profiles["charlie"]  → KeyError!

Add, update, and loop over .items()

profiles = {"alice": 90, "bob": 70}
profiles["carol"] = 88     # add a new key
profiles["alice"] = 95     # update an existing key

for name, score in profiles.items():
    print(f"{name}: {score}")
# Output:
# alice: 95
# bob: 70
# carol: 88

in checks keys; find the top scorer

def top_scorer(profiles):
    best_user = None
    best_score = -1
    for name, score in profiles.items():
        if score > best_score:
            best_score = score
            best_user = name
    return best_user

p = {"alice": 90, "bob": 70, "charlie": 95}
print("bob" in p)        # True   (checks keys)
print(top_scorer(p))     # charlie

Common mistakes & gotchas

d[missing] raises KeyError

Indexing a key that isn't there crashes with KeyError. When a key might be absent, use d.get(key) (returns None) or d.get(key, default). Check first with if key in d: when you need to branch.

in checks keys, not values

"bob" in profiles tests the *keys*. To check values, use 90 in profiles.values(). Forgetting this leads to membership tests that quietly always fail.

Keys must be hashable; duplicates collapse

Keys have to be immutable (str, int, tuple — not list or dict). And a dict can't hold two of the same key: {"a": 1, "a": 2} keeps only {"a": 2} — the last assignment wins.

Why it matters

Dictionaries are everywhere data has structure: API responses, config, JSON, counting and grouping, in-memory caches and lookups. The `.get()`-with-default habit alone prevents a whole category of `KeyError` crashes, and `.items()` is how you iterate structured records cleanly.