Week 10 — Coding Lab / Programming Studio · "Map It, Dedupe It, Lock It"
Course: Introduction to Computer Science — CS1 / Programming Fundamentals in Python (CSCI 1101) · Silver Oak University (fictional sample) · Prof. Okafor
Objective: Objective 6 — build a dictionary and a set; trace dict/set/tuple operations and predict output; find & fix a KeyError and a tuple-immutability bug · SLO A (write & run) + SLO B (trace & debug)
Worth 50 points · Coding Labs group = 15% of the grade · Coding Lab 10
Format: a hands-on programming studio worked in a free online Python environment — you'll (a) build a dictionary and a set, (b) trace dict/set/tuple operations and predict their output, and (c) find and fix a KeyError and a tuple-mutation bug — then visualize a dictionary in Python Tutor and catch the AI's mistake when it assumes a set keeps order or invents a dict value.
This is the course's signature weekly component. Every instructional week has one Coding Lab. Everything runs in your browser — nothing to buy or install. The whole habit of this lab (and this course): don't guess what code does — run it and read what Python actually prints. This week that habit is the only reliable way to settle a
KeyErrorand a set's order.
Part 1 — The Big Picture
This week you met the other three core collections — tuples (immutable), dictionaries (look up by key), and sets (unique + unordered) — plus the headline skill of choosing the right one. This lab puts the whole week through its paces: you'll build a dictionary and a set, trace dict/set/tuple operations (predict, then run), and debug the two signature crashes (a KeyError and a tuple mutation). By the end you'll have watched a dictionary fill up key by key in Python Tutor — the clearest way to see what a dictionary is.
Your tools (both free, both in the browser):
- An online Python editor to write and run code: 🔗 https://www.online-python.com/ (or 🔗 https://www.programiz.com/python-programming/online-compiler)
- Python Tutor to watch code run step by step — and to see a dictionary grow: 🔗 https://pythontutor.com/ — paste a program, click Visualize Execution, then step forward and watch the key→value box.
Part 2 — Setup (2 minutes)
- Open the online Python editor in a new tab.
- Delete any sample code so you have a blank editor.
- Type and run:
python phone = {"Sam": 5551234} phone["Ada"] = 5559876 print(phone["Ada"])
You should see5559876. You just built a dictionary and looked someone up by name. 🎉
Part 3 — (a) Build: Your Collection Programs
Write each of these in the editor, run it, and paste your code and its output into your submission.
- Build a phone book. Create a dictionary with two people and their numbers (e.g.,
phone = {"Sam": 5551234, "Ada": 5559876}). Then add a third person, update one person's number, look up one person withphone[name], and printlen(phone). Print the dictionary after your changes and write down what you saw. - Count words with a dictionary. Start from
words = ["red", "blue", "red", "red", "blue"]. Use aforloop to build a dictionarycountswhere each word maps to how many times it appears (hint:counts[w] = counts.get(w, 0) + 1). Printcounts. (You should see each word counted.) - Dedupe with a set. Make a list with several repeats (e.g.,
tags = ["py", "cs", "py", "ai", "cs", "py"]). Printset(tags)to see the duplicates vanish, and printlen(set(tags))to count the unique tags. Run it and note both results — and notice you can't predict the order the set prints in.
Part 4 — (b) Trace: Predict, Then Run
For each program below, first write your predicted output in the table (don't run it yet!). Then run each one in the editor and fill in the "Actual" column. Where your prediction and the actual output differ, that's the lesson. Rows 1–6 start from d = {"a": 1, "b": 2, "c": 3}.
| # | Program | Your prediction | Actual (after running) |
|---|---|---|---|
| 1 | d = {"a": 1, "b": 2, "c": 3} → print(d["b"]) |
______ | ______ |
| 2 | d = {"a": 1, "b": 2, "c": 3} → print(d.get("z", 0)) |
______ | ______ |
| 3 | d = {"a": 1, "b": 2, "c": 3} → print(list(d.keys())) |
______ | ______ |
| 4 | d = {"a": 1, "b": 2, "c": 3} → print("b" in d) |
______ | ______ |
| 5 | d = {"a": 1, "b": 2, "c": 3} → d["a"] = 9 → print(d["a"]) |
______ | ______ |
| 6 | d = {"a": 1, "b": 2, "c": 3} → d["e"] = 5 → print(len(d)) |
______ | ______ |
| 7 | print(set([4, 4, 5, 6, 6])) |
______ | ______ |
| 8 | print(len({2, 4, 4, 6})) |
______ | ______ |
| 9 | print(7 in {1, 2, 7}) |
______ | ______ |
| 10 | pt = (10, 20) → print(pt[1]) |
______ | ______ |
Hint for #2:
.get(key, default)doesn't crash on a missing key — what's the0for?
Hint for #5 vs #6: updating an existing key (d["a"]) doesn't change the number of keys; adding a new key (d["e"]) does. Predictlencarefully.
Hint for #7 and #8: focus on which values survive (duplicates are dropped) — don't stress about the order.
Visualize the headline: paste this into Python Tutor and step through it:
counts = {}
for w in ["red", "blue", "red"]:
counts[w] = counts.get(w, 0) + 1
print(counts)
Watch the dictionary box gain 'red': 1, then 'blue': 1, then watch 'red' tick up to 2. That growing key→value box is what a dictionary is.
Part 5 — (c) Find & Fix the Bug
Each program below crashes. For each one: run it, read the error (bottom line first!), then write (i) the error type, (ii) why it happened, and (iii) the fixed program with its correct output.
Bug A — the missing key. This is supposed to print Ada's score, but Ada isn't in the dictionary. Make it print no score instead of crashing.
scores = {"Sam": 88, "Mo": 72}
print(scores["Ada"])
Bug B — the locked pair. This is supposed to change the first color to "yellow", but it crashes.
colors = ("red", "green", "blue")
colors[0] = "yellow"
Part 6 — Analysis Questions
Answer in a sentence or two each:
1. In the trace table, which prediction did you get wrong (if any)? What did you learn from the gap? (Rows #2 and #6 are the usual surprises.)
2. In #5 (d["a"] = 9) and #6 (d["e"] = 5), one changed a value and the other changed the number of keys. Which did which, and why does len(d) only change in one of them?
3. For Bug A, why did scores["Ada"] crash instead of returning None? Name two different ways to look up a key that might be missing without crashing.
4. For Bug B, what does it mean that a tuple is immutable, and how do you "change" the first value if you can't assign to colors[0]?
5. Choose the right collection. For each job, name the best collection and one reason: (a) store each student's grade so you can look it up by name; (b) keep only the unique words that appeared in an essay; (c) a fixed (latitude, longitude) pair that should never change.
Part 7 — AI-Critique Moment (required — this is the BYOAI step)
Now bring in your approved chatbot (Gemini, Claude, or ChatGPT) and be the programmer who checks its work. This week the model has two favorite blind spots: set ordering and pretending a missing key is fine.
- Paste this to the chatbot: "What does each of these Python programs print? (a)
print({3, 1, 2}); (b)ages = {'Sam': 20}thenprint(ages['Ada']); (c)print({'x': 1}.get('y', 0)). Give me the exact output of each." - Check every claim by running each program yourself in the editor:
- For (a): did it state the set prints in a confident, specific order (e.g., "in the order you typed it" or "sorted")? A set is unordered — the order isn't something to trust. (Run it a few times if your editor allows; the lesson is "don't rely on a set's order," not any single printed order.)
- For (b): did it invent a number or say it printsNone? It actually crashes with aKeyError. Chatbots very often get this wrong, pretending the lookup just returns nothing.
- For (c): did it correctly say0(the.get()default), since'y'is missing? - Write 2–3 sentences reporting what the AI got right and at least one thing you had to correct or verify carefully (most likely the
KeyErrorin (b) or the set-order claim in (a)). If it happened to get everything right, say how you confirmed each one by running it — that's the skill.
The habit all term: the tool drafts, you run it and judge. A chatbot will confidently tell you a set's order or hand you a value for a key that doesn't exist — catching that by running the code is the entire point of this week.
Part 8 — What to Submit
Submit a single document (or text entry) with: your Part 3 programs and their outputs; your completed Part 4 trace table (both columns); your Part 5 bug answers (error type, why, and the fixed program + output for each); your Part 6 answers; and your Part 7 AI-critique paragraph. Due Sunday, Nov 8, 11:59 p.m. (50 points).
Instructor answer key & model outputs — REMOVE BEFORE PUBLISHING TO STUDENTS
Execution gate: PASS — every output below was produced by actually running the code in Python. Students' Part 3 wording varies; grade those on "does it run and produce the right kind of output." Note: the set examples in this lab were chosen so their print order is stable, but the teaching point is "duplicates gone, order not guaranteed."
Part 3 (model):
1. A phone book built, mutated, and looked up, e.g. phone = {"Sam": 5551234, "Ada": 5559876} → phone["Mo"] = 5552020 → phone["Sam"] = 5550000 → phone["Ada"] is 5559876, len(phone) is 3. ✓ (numbers/names vary; check add + update + lookup + len)
2. counts.get(w, 0) + 1 loop over ["red", "blue", "red", "red", "blue"] → {'red': 3, 'blue': 2}. ✓ (an if/else version is equally correct)
3. set(tags) drops duplicates; len(set(tags)) is the unique count — for ["py", "cs", "py", "ai", "cs", "py"], len(set(tags)) is 3. ✓ (the printed set's order may vary; only the unique count matters)
Part 4 trace table (run-verified):
| # | Program | Actual output |
|---|---|---|
| 1 | print(d["b"]) |
2 |
| 2 | print(d.get("z", 0)) |
0 |
| 3 | print(list(d.keys())) |
['a', 'b', 'c'] |
| 4 | print("b" in d) |
True |
| 5 | d["a"] = 9 → print(d["a"]) |
9 |
| 6 | d["e"] = 5 → print(len(d)) |
4 |
| 7 | print(set([4, 4, 5, 6, 6])) |
{4, 5, 6} |
| 8 | print(len({2, 4, 4, 6})) |
3 |
| 9 | print(7 in {1, 2, 7}) |
True |
| 10 | pt = (10, 20) → print(pt[1]) |
20 |
Part 5 bugs (run-verified):
- Bug A (KeyError): (i) KeyError (KeyError: 'Ada'); (ii) "Ada" is not a key in scores, and square-bracket lookup on a missing key crashes (it does NOT return None); (iii) fix with .get() and a default (or an in check):
python
scores = {"Sam": 88, "Mo": 72}
print(scores.get("Ada", "no score"))
Output (run-verified): no score. (An if "Ada" in scores: ... else: print("no score") version also works.)
- Bug B (tuple immutability): (i) TypeError ('tuple' object does not support item assignment); (ii) a tuple is immutable — you can't assign to one of its elements; (iii) fix by building a new tuple:
python
colors = ("red", "green", "blue")
colors = ("yellow", "green", "blue")
print(colors)
Output (run-verified): ('yellow', 'green', 'blue').
Part 6 (expected): (1) most commonly #2 (.get("z", 0) returns the default 0, not a crash) or #6 (len becomes 4 because a NEW key was added). (2) Row #5 updates an existing key (d["a"] = 9 only changes a value, so the key count is unchanged), while row #6 adds a new key (d["e"] = 5, taking the dictionary from 3 keys to 4) — so len(d) changes only in #6. The rule: assigning to an existing key changes the value only; assigning to a new key adds a pair and increases len. (3) scores["Ada"] crashes because "Ada" isn't a key and [] lookup raises KeyError; two safe ways: scores.get("Ada") / scores.get("Ada", default), or if "Ada" in scores: first. (4) Immutable = can't be changed after creation; you can't do colors[0] = ..., so you build a new tuple colors = ("yellow", "green", "blue"). (5) (a) dict — look up grade by student name (key→value); (b) set — keeps only unique words; (c) tuple — a fixed pair that shouldn't change.
Part 7 (AI-critique): full credit for a specific catch — most commonly the AI saying print(ages['Ada']) returns None or some number (it's a KeyError), or asserting a confident order for the set in (a). Full credit also if the student verified each AI claim by running it.
Grading rubric — 50 points
| Criterion | Full | Partial | None |
|---|---|---|---|
| Part 3 — built & ran 3 programs (phone-book dict mutated + looked up; word-count dict; set dedup with unique count) (14) | 14 | 7–11 | 0–5 |
Part 4 — trace table (predictions attempted + all 10 actual outputs correct from running, incl. .get default and the len change) (14) |
14 | 7–11 | 0–5 |
Part 5 — found & fixed both bugs (error type + why + fix + correct output for the KeyError AND the tuple mutation) (12) |
12 | 6–10 | 0–4 |
Part 6 — analysis (update-vs-add len; KeyError and two safe lookups; tuple immutability; which-collection choices) (6) |
6 | 3–5 | 0–2 |
Part 7 — AI-critique (names a specific thing checked/corrected by running — ideally the KeyError or set-order miss) (4) |
4 | 2 | 0–1 |
Quality gate (self-checked): every model output above (5559876, {'red': 3, 'blue': 2}, the unique count 3, the table values 2, 0, ['a', 'b', 'c'], True, 9, 4, {4, 5, 6}, 3, True, 20, and both bug fixes no score and ('yellow', 'green', 'blue')) was produced by actually running the code in Python — execution gate: PASS. No output is hand-traced (the KeyError and the tuple TypeError are exactly the kind a hand-trace gets wrong by assuming None or a silent change). Every set printed in this lab was chosen for stable order, but the lab teaches that a set's order is not to be relied on. The lab grades the student's process (build → trace → debug → see-it-in-Python-Tutor → critique the AI), not a single fixed wording for the open-ended Part 3.
~ Prof. Okafor's edition · Fall 2026 · built with thecoursemaker.com