← Course Home
Module 3

Dict

Key-value mappings — how real API data actually looks. Every Okta/Slack/Google response you'll ever pull is built out of these.

Concept

Making a Dict

A dict maps a key to a value — like a labeled lookup table. Curly braces, key: value pairs, separated by commas.

user = {"name": "Julio", "status": "ACTIVE"}
print(user)
Lab 1

Create a dict person with "name": "Amy", then print it.

person = {"name": "Amy"}
print(person)
Lab 2

Create a dict scores with "math": 90 and "art": 85, then print it.

scores = {"math": 90, "art": 85}
Lab 3

Create an empty dict called settings, then print it.

settings = {}{}
Lab 4

Create a dict info with "active": True and "count": 5, then print it.

Lab 5

Create a dict user with "id": "00u001" and "status": "ACTIVE", then print it.

Now go practice

Open problems/01_create.py and do all 5 problems locally.

Concept

Printing a Dict

Printing a dict shows its exact structure — braces, keys, and values, in the order you created them.

print({"a": 1})   # {'a': 1}
Lab 1

Print {"x": 1, "y": 2} directly.

{'x': 1, 'y': 2}
Lab 2

Print {"active": True}.

{'active': True}
Lab 3

Print a dict with keys in the order "z", "a", "m" — notice the print keeps that order, not alphabetical.

{'z': 1, 'a': 2, 'm': 3}
Lab 4

Print an empty dict.

{}
Lab 5

Print a dict with one key holding a list as its value: {"tags": ["a", "b"]}.

{'tags': ['a', 'b']}
Now go practice

Open problems/02_print.py and do all 5 problems locally.

Concept

Getting a Value by Key

dict["key"] looks like list indexing, but it works differently — you give the key, not a position.

user = {"id": "00u001", "status": "ACTIVE"}
print(user["status"])   # ACTIVE
Lab 1

person = {"name": "Amy", "role": "Admin"}. Print person["name"].

Amy
Lab 2

Print person["role"].

Admin
Lab 3

scores = {"math": 90, "art": 85}. Print scores["math"].

90
Lab 4

Print scores["art"].

85
Predict first

user = {"id": "00u001", "status": "ACTIVE"}. What happens with print(user["department"]) — a key that isn't there?

Check
CrashesKeyError: 'department'

Same family as IndexError from lists — asking for something that doesn't exist. Your fixture has a user (00u008) missing the department key entirely — this is exactly that bug.

Lab 5

user = {"id": "00u001", "status": "ACTIVE"}. Print user["id"] — a key you know exists.

00u001
Lab 6

Create your own 2-key dict and print one of the values by key.

Now go practice

Open problems/03_access_by_key.py and do all 6 problems locally.

Concept

Safe Access (.get())

.get() asks for a key without crashing if it's missing — it just gives you None instead.

user = {"id": "00u001"}
print(user.get("department"))   # None -- no crash
Lab 1

user = {"id": "00u002"}. Print user.get("id") — a key that exists.

00u002
Lab 2

Print user.get("department") — a key that doesn't exist.

None
Lab 3

Compare: predict what user["department"] would do (crash) vs. what you just saw with .get() (no crash).

Lab 4

scores = {"math": 90}. Print scores.get("art").

None
Lab 5

Print bool(scores.get("art")) — combining with Module 1's truthiness.

False (None is falsy)
Lab 6

Print scores.get("math") is None, then scores.get("art") is None — two lines.

False then True
Now go practice

Open problems/04_get.py and do all 6 problems locally.

Concept

Safe Access With a Fallback

.get(key, default) lets you choose what comes back instead of None when the key is missing.

user = {"id": "00u001"}
print(user.get("department", "Unassigned"))   # Unassigned
Lab 1

user = {"id": "00u002"}. Print user.get("department", "Unassigned").

Unassigned
Lab 2

user = {"id": "00u003", "department": "Sales"}. Print user.get("department", "Unassigned") — key exists, default is ignored.

Sales
Lab 3

scores = {"math": 90}. Print scores.get("art", 0).

0
Lab 4

Print scores.get("math", 0) — key exists this time.

90
Lab 5

Get a missing key with default False instead of a string, print it.

False
Lab 6 · Checkpoint

One script: create a dict, print it, access a key directly, use .get() on a missing key, and use .get() with a default on the same missing key. Four lines of output.

Combines sub-skills 1–5.
Now go practice

Open problems/05_get_default.py — 6 problems, #6 is a checkpoint combining sub-skills 1–5.

Concept

Length

len() — same function as always — counts the number of key-value pairs.

user = {"id": "00u001", "status": "ACTIVE"}
print(len(user))   # 2
Lab 1

Print len({"a": 1, "b": 2, "c": 3}).

3
Lab 2

Print len({}).

0
Lab 3

user = {"id": "00u001"}. Add nothing yet — just print len(user).

1
Lab 4

Print len({"x": 1}) vs len(["x"]) — a 1-key dict and a 1-item list, same length.

1 then 1
Lab 5

Print len({"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}).

5
Now go practice

Open problems/06_length.py and do all 5 problems locally.

Concept

Is That Key In There? (in)

in on a dict checks the keys, not the values — even if a value matches, in won't find it unless it's a key.

user = {"status": "ACTIVE"}
print("status" in user)    # True -- "status" is a key
print("ACTIVE" in user)    # False -- "ACTIVE" is a value, not a key
Lab 1

user = {"id": "00u001", "status": "ACTIVE"}. Print "status" in user.

True
Lab 2

Print "department" in user.

False
Lab 3

Print "ACTIVE" in user — a value, not a key.

False
Lab 4

Print "id" not in user.

False
Lab 5

Print "x" in {} — an empty dict.

False
Lab 6

Use in to decide what .get() would have told you: print "status" in user, then print user.get("status") — two lines.

True then ACTIVE
Predict first

user = {"id": "00u001", "status": "ACTIVE"}. Is "00u001" in user — checking a valueTrue or False?

Check
False

"00u001" is a value here, not a key — in only ever checks keys on a dict.

Now go practice

Open problems/07_membership.py and do all 7 problems locally.

Concept

Adding a New Key

dict[key] = value creates the key if it doesn't exist yet.

user = {"id": "00u001"}
user["status"] = "ACTIVE"
print(user)   # {'id': '00u001', 'status': 'ACTIVE'}
Lab 1

person = {"name": "Amy"}. Add "role": "Admin", print person.

{'name': 'Amy', 'role': 'Admin'}
Lab 2

Add a second new key "active": True too, print again.

Lab 3

settings = {}. Add "theme": "dark", print settings.

{'theme': 'dark'}
Lab 4

After adding a key, print len() to confirm it grew.

Lab 5

Add a key, then confirm with in that it's there.

True
Lab 6

Add a key, then read it back with .get() to confirm the value.

Lab 7

Start with an empty dict, add three different keys one at a time, print the final dict.

Now go practice

Open problems/08_add.py and do all 7 problems locally.

Concept

Updating an Existing Key

Same syntax as adding — dict[key] = value — but this time the key already exists, so it overwrites instead of creating.

user = {"status": "ACTIVE"}
user["status"] = "SUSPENDED"
print(user)   # {'status': 'SUSPENDED'}
Lab 1

user = {"status": "ACTIVE"}. Update "status" to "SUSPENDED", print user.

{'status': 'SUSPENDED'}
Lab 2

scores = {"math": 90}. Update "math" to 95, print scores.

{'math': 95}
Lab 3

Update a key, then confirm len() stayed the same (updating doesn't add a key).

Lab 4

user = {"status": "ACTIVE"}. Print the value before and after updating "status" to "DEPROVISIONED" — two lines.

ACTIVE then DEPROVISIONED
Lab 5

Update a key to hold a completely different type than before (string → number), print the result.

Lab 6

Update the same key twice in a row, print only the final value.

Lab 7

Simulate a deprovision: user = {"status": "ACTIVE"}, update to "SUSPENDED", then update again to "DEPROVISIONED". Print the final dict.

Now go practice

Open problems/09_update.py and do all 7 problems locally.

Concept

Removing a Key (.pop())

dict.pop(key) removes a key and hands back its value. This is the same method name as list.pop(), but it takes a key here, not a position — same word, different job.

user = {"id": "00u001", "status": "ACTIVE"}
removed = user.pop("status")
print(removed)   # ACTIVE
print(user)       # {'id': '00u001'}
Lab 1

user = {"id": "00u001", "status": "ACTIVE"}. Pop "status" into a variable, print the variable.

ACTIVE
Lab 2

After popping, print user — confirm the key is gone.

{'id': '00u001'}
Lab 3

Pop a key, then print len() to confirm it shrank.

Lab 4

Pop a key, then confirm with in that it's gone.

False
Lab 5

d = {"a": 1}. Pop "a" with a default: d.pop("b", "not found") — key that doesn't exist, but a default is given.

not found, no crash
Predict first

d = {"a": 1}. What happens with d.pop("b") — no default this time?

Check
CrashesKeyError: 'b'

Just like accessing a missing key with [] — without a default, .pop() on a missing key crashes too.

Lab 6

Compare: colors = ["red", "blue"], pop position 0, print what came back. user = {"a": 1}, pop key "a", print what came back. Same method name, different kind of argument.

red then 1
Lab 7

Add a key, then immediately pop it back off, print the dict before and after (should match).

Lab 8 · Checkpoint

One script: create a dict with 2 keys, add a third key, update one of the original two, pop the third key back off, print what was popped and the final dict.

Combines sub-skills 6–10.
Now go practice

Open problems/10_pop.py — 8 problems, #8 is a checkpoint combining sub-skills 6–10.

Concept

Is It Empty?

Same truthiness rule from Module 1: an empty dict is falsy. bool(my_dict) tells you if it has anything in it.

settings = {}
print(bool(settings))   # False -- empty
Lab 1

Print bool({}).

False
Lab 2

Print bool({"a": 1}) — one key.

True
Lab 3

d = {"a": 1}. Pop the only key, then print bool(d).

False
Lab 4

Compare len(d) == 0 to bool(d) on the same empty dict.

True and False, both correctly say "empty"
Lab 5

Start with an empty dict, add a key, print bool() before and after adding.

False then True
Lab 6

Create a dict with 1 key, pop it by name, print bool() of the result.

False
Now go practice

Open problems/11_empty_check.py and do all 6 problems locally.

Concept

A Dict Inside a Dict

A value can be another dict. Chain the brackets to reach in: user["profile"]["email"] means "get profile, then get email from that."

user = {"id": "00u001", "profile": {"email": "jrivera@example.com", "department": "IT"}}
print(user["profile"]["email"])   # jrivera@example.com
Lab 1

user = {"profile": {"email": "achen@example.com"}}. Print user["profile"]["email"].

achen@example.com
Lab 2

Print user["profile"] alone first — see it's a whole dict, not a string.

{'email': 'achen@example.com'}
Lab 3

user = {"profile": {"department": "Security"}}. Print user["profile"]["department"].

Security
Lab 4

Use .get() on the outer dict, then index the result: user.get("profile")["department"].

Security
Lab 5

Store profile = user["profile"] in its own variable first, then print profile["department"] — same result, split into two steps.

Lab 6

Predict: your fixture's 00u008 has no department key inside its profile. What would user["profile"]["department"] do for that record?

CrashesKeyError: 'department'
Lab 7

Safer version of the above: use .get() on the inner dict instead — user["profile"].get("department", "Unknown").

no crash
Lab 8

Build your own 2-level nested dict from scratch and print one deeply-nested value.

Now go practice

Open problems/12_nested_dict.py and do all 8 problems locally.

Concept

A Dict Containing a List

A value can be a list too. Get the list with a key, then index into it like any list.

user = {"id": "00u001", "groups": ["Engineering", "VPN-Users"]}
print(user["groups"][0])   # Engineering
Lab 1

user = {"groups": ["Sales", "IT"]}. Print user["groups"][0].

Sales
Lab 2

Print user["groups"][-1] — the last group.

IT
Lab 3

Print len(user["groups"]) — how many groups.

2
Lab 4

Print "IT" in user["groups"].

True
Lab 5

user = {"groups": []}. Print bool(user["groups"]) — like a user with zero groups in your fixture.

False
Lab 6

Store groups = user["groups"] first, then .append() a new group to it, print groups.

Lab 7

Print a slice of the groups list: user["groups"][0:1].

Lab 8

Build your own dict with a 3-item list value, print the middle item by index.

Now go practice

Open problems/13_nested_list.py and do all 8 problems locally.

Concept

Merging Two Dicts (.update())

.update() merges another dict's keys and values in. Matching keys get overwritten; new keys just get added.

a = {"id": "00u001", "status": "ACTIVE"}
b = {"status": "SUSPENDED", "department": "IT"}
a.update(b)
print(a)   # {'id': '00u001', 'status': 'SUSPENDED', 'department': 'IT'}
Lab 1

a = {"x": 1}, b = {"y": 2}. Update a with b, print a.

{'x': 1, 'y': 2}
Lab 2

a = {"x": 1}, b = {"x": 99}. Update a with b, print a — matching key gets overwritten.

{'x': 99}
Lab 3

Update a dict with an empty dict, print the result — nothing changes.

Lab 4

Merge, then print len() of the result.

Lab 5

Merge, then check in for a key that only existed in the second dict.

True
Lab 6

Build a dict via [key] = assignment (Module 3's "add"), a second dict as a literal, merge them.

Lab 7

Merge two dicts, then .get() a key from the result with a default.

Lab 8

Merge, then pop one of the merged-in keys back off, print the final dict.

Lab 9

Merge a dict containing a nested dict value with another dict, print the result and access the nested value.

Lab 10 · Final checkpoint

One script using every sub-skill 1–14 at least once: create, print, access by key, .get(), .get() with default, length, membership, add, update, pop, empty-check, nested dict, nested list, and merge — ending in one printed summary line.

This is the big one — take your time.
Now go practice

Open problems/14_merge.py — 10 problems, #10 is the final checkpoint for all of Module 3.

Complete

Module 3 done

create → print → access → get → get-default → length → membership → add → update → pop → empty-check → nested dict → nested list → merge.

Run python check.py in problems/ to grade everything.