Week 10 — Lecture Outline · Tuples, Dictionaries & Sets
Course: Introduction to Computer Science — CS1 / Programming Fundamentals in Python (CSCI 1101) · Silver Oak University (fictional sample) · Prof. Okafor
Objective covered: Objective 6 — Use Python's core collections; this week: tuples (and immutability), dictionaries (key → value lookup, .get(), KeyError), and sets (uniqueness/membership), and choose the right collection.
SLOs touched: A (write and run a correct program) · B (trace code and predict dict/set/tuple results; find and fix a defect)
Meeting pattern: 2 studio sessions × 75 min = 150 min. Segment minutes below total ~150; scale to your own pattern.
Every code output, traced value, and error message in this outline was produced by actually running the code in Python — not hand-traced. Run them live in class; the outputs are exact. (The set-printing results are the kind most people — and most chatbots — get wrong by assuming order; we ran them. Note: a set's print order is not something to memorize — sets are unordered; the small-integer sets shown here happen to print in a stable order, but the lesson is "duplicates are gone," not "this is the order.")
Week at a Glance
| The week's big question | "When you have a pile of data, which container should you reach for — a list, a tuple, a dictionary, or a set — and how do you look things up without crashing?" |
| By the end of the week, students can… | (1) use a tuple (a, b), index and unpack it, and explain immutability (assigning to an element raises a TypeError); (2) build a dictionary {key: value}, look up d[key], add/update, use .get(), .keys(), .values(), .items(), and iterate it; (3) avoid a KeyError with .get() or in; (4) use a set to dedupe and test membership, and explain why sets are unordered; (5) choose list vs. tuple vs. dict vs. set for a job. |
| Key vocabulary | tuple, immutable, immutability, TypeError, unpacking, dictionary/dict, key, value, key-value pair, lookup, d[key], add/update, .get(), default, .keys(), .values(), .items(), in (membership), KeyError, set, unique, dedup/deduplicate, unordered, set(), choosing a collection |
| Materials | slides (Deck 10), the week's readings + video links, a free online Python environment + Python Tutor (links in the readings), one approved chatbot (Gemini / Claude / ChatGPT) for the AI-critique moment and the tutorial |
| Timing note | 8 segments, ~150 min total. Session 1 = Segments 1–4 (~75). Session 2 = Segments 5–8 (~75). This is a studio: build the dictionary and set on the projector and have students type every example into the online environment with you, then run it. |
Segment 1 — Hook & the Promise (8 min) · Session 1 opens
Hook. Hold up a phone book (or put a picture of one on the slide). "If I want Ada's number, do I read the book from page 1 looking at every number until I find hers? No — I look her up by name." That's the whole idea of a dictionary: you don't find things by position (like a list's index), you find them by a key (a name, a word, an ID). Then put up a guest list with a name written twice and crossed out once: "A guest list doesn't need duplicates — each person counts once." That's a set. And a coordinate (40.7, -74.0): "These two numbers belong together and should never change." That's a tuple.
The promise (write it on the board): "By Friday you'll build a phone book in code and look people up by name, dedupe a messy list down to its unique values in one line, and crash a program on purpose two ways — then fix both. And you'll know the question a real programmer asks first: which collection fits this job?"
Why it matters line (memory hook): "A list finds things by position; a dictionary finds things by name (a key)."
Segment 2 — Tuples & Immutability (18 min)
Plain language first. A tuple is like a list — an ordered group of values — but written with parentheses instead of square brackets, and with one giant difference: a tuple can't be changed after you make it. That property has a name: it's immutable. Use a tuple when a group of values belongs together and should stay fixed — a point (x, y), a date (year, month, day), an RGB color (255, 0, 0).
Build and read one (code-along — everyone types and runs):
point = (3, 4)
print(point)
print(point[0])
print(point[1])
print(len(point))
Output (run-verified):
(3, 4)
3
4
2
"Indexing is exactly like a list and a string — point[0] is the first item. The parentheses are the only syntax change."
Unpacking — pull a tuple apart into names (very common):
point = (3, 4)
x, y = point
print(x)
print(y)
Output (run-verified):
3
4
"x, y = point hands the first value to x and the second to y in one line. You've actually been doing tuple unpacking since Week 2 every time you wrote a, b = 1, 2."
The signature move — try to change it, and watch Python refuse (run it!):
point = (3, 4)
point[0] = 9
Run-verified error:
TypeError: 'tuple' object does not support item assignment
"A list would have happily let us do nums[0] = 9 last week. A tuple says no — that's immutability. The error type is a TypeError: we tried to do something to a type that doesn't allow it. If you need to 'change' a tuple, you build a new one: point = (9, 4)."
The one gotcha to name (a single-item tuple needs a comma):
single = (5,)
print(len(single))
Output (run-verified):
1
"(5) is just the number 5 in parentheses; (5,) — with the trailing comma — is a one-item tuple. Rare, but it surprises people."
Segment 3 — Dictionaries: Look Up by Key (the code-along) (26 min)
Set it up: "Now the most useful collection in Python: the dictionary. Open the editor — we're going to build a phone book and look people up by name."
Plain language first. A dictionary stores pairs: each pair is a key and a value. You write it with curly braces and colons: {key: value, key: value}. You look up a value by its key — phone["Sam"] — the same way you'd flip to "Sam" in a phone book. Keys are usually strings or numbers; values can be anything.
Build a phone book and look people up (code-along, step by step):
Step 1 — create it and print it:
phone = {"Sam": 5551234, "Ada": 5559876}
print(phone)
Output (run-verified):
{'Sam': 5551234, 'Ada': 5559876}
Step 2 — look up a value by its key:
print(phone["Sam"])
Output:
5551234
"phone["Sam"] means 'give me the value stored under the key Sam.' This is the dictionary's whole job — fast lookup by name."
Step 3 — add a new pair (just assign to a new key):
phone["Mo"] = 5552020
print(phone)
Output:
{'Sam': 5551234, 'Ada': 5559876, 'Mo': 5552020}
Step 4 — update an existing key (assigning to a key that already exists replaces its value):
phone["Sam"] = 5550000
print(phone)
Output:
{'Sam': 5550000, 'Ada': 5559876, 'Mo': 5552020}
"Adding and updating look identical — dict[key] = value. If the key is new, it's added; if it already exists, its value is replaced. There's only ever one value per key."
Step 5 — len, membership with in, and the safe lookup .get():
print(len(phone))
print("Ada" in phone)
print("Kai" in phone)
print(phone.get("Ada"))
print(phone.get("Kai"))
Output (run-verified):
3
True
False
None
Wait — that's only four lines of output for five prints. The fifth, phone.get("Kai"), printed a blank line? No — print(None) shows the word None. Here is the full run-verified output:
3
True
False
5559876
None
"in checks whether a key is present (not a value). .get("Kai") returns None for a missing key instead of crashing — that's the seatbelt we'll need in Segment 5."
The dictionary cheat-sheet — put it on a slide:
| Operation | Code | Result |
|---|---|---|
| look up a value | d[key] | the value (or KeyError if missing) |
| add / update | d[key] = value | adds if new, replaces if it exists |
| safe lookup | d.get(key) / d.get(key, default) | value, else None / default |
| is this key here? | key in d | True / False |
| how many pairs | len(d) | the count |
| all the keys / values | d.keys() / d.values() | the keys / the values |
Segment 4 — Iterating a Dictionary (16 min) · Session 1 closes (~75)
Set it up: "A dictionary is most useful when you walk through it. Looping over a dictionary gives you its keys."
Iterate the keys (code-along):
ages = {"Sam": 20, "Ada": 22}
for name in ages:
print(name)
Output (run-verified):
Sam
Ada
"A plain for name in ages: hands you each key. (Dictionaries remember the order you inserted keys — that's a guarantee since Python 3.7 — so this order is reliable. A set will not give you that, as we'll see.)"
Get the value too, with .items():
ages = {"Sam": 20, "Ada": 22}
for name, age in ages.items():
print(name, age)
Output (run-verified):
Sam 20
Ada 22
.keys() and .values() (wrap in list() to see them plainly):
ages = {"Sam": 20, "Ada": 22}
print(list(ages.keys()))
print(list(ages.values()))
Output (run-verified):
['Sam', 'Ada']
[20, 22]
Worked trace — predict, then run (build a word-count dictionary): "We count how many times each color appears."
words = ["red", "blue", "red", "red", "blue"]
counts = {}
for w in words:
counts[w] = counts.get(w, 0) + 1
print(counts)
- Trace: start with an empty dict
{}."red":counts.get("red", 0)is0, socounts["red"] = 1."blue":0 + 1 = 1."red"again:counts.get("red", 0)is now1, so2."red"again →3."blue"again →2. - Predicted output:
{'red': 3, 'blue': 2}. Run it. Actual output (run-verified):
{'red': 3, 'blue': 2}
"This counts.get(w, 0) + 1 pattern — 'whatever's there, or 0 if it's new, plus one' — is the single most common dictionary idiom in all of programming: counting/tallying. .get(w, 0) is what keeps the first time we see a word from crashing with a KeyError."
Segment 5 — The KeyError (and how .get() saves you) (20 min) · Session 2 opens
Hook back in: "Last session you built a phone book. Today you'll crash it on purpose — because the KeyError is the dictionary bug, and the fix is a one-liner you'll use forever."
Plain language first. If you look up a key that isn't in the dictionary with square brackets, Python doesn't return blank or None — it stops and raises a KeyError.
Live demo — the crash (run it):
ages = {"Sam": 20}
print(ages["Ada"])
Run-verified error:
KeyError: 'Ada'
"'Ada' isn't a key, so Python raises KeyError: 'Ada'. Read it bottom-up like every error: the last line names it. This is the trap everyone's first guess gets wrong — people expect None. Square-bracket lookup on a missing key crashes."
Fix #1 — .get() with a default (no crash):
ages = {"Sam": 20}
print(ages.get("Ada"))
print(ages.get("Ada", 0))
Output (run-verified):
None
0
".get("Ada") returns None; .get("Ada", 0) returns the fallback 0. Use .get() whenever a key might be missing."
Fix #2 — check with in before you look up:
ages = {"Sam": 20}
if "Ada" in ages:
print(ages["Ada"])
else:
print("not found")
Output (run-verified):
not found
Misconception + cure:
- ❌ "Looking up a missing key gives me None."
✅ Cure: square brackets crash with a KeyError. Only .get() returns None. When in doubt, run it — print(ages["Ada"]) vs print(ages.get("Ada")) are two different stories.
Segment 6 — Sets: Uniqueness, Dedup & Membership (18 min)
Set it up: "Last collection: the set. A set is an unordered bag that automatically refuses duplicates. Two superpowers: dedupe a list in one line, and lightning-fast membership tests."
Dedupe — the signature one-liner (code-along, predict first):
print({1, 2, 2, 3})
- Predicted: the set drops the repeat. Run it. Actual output (run-verified):
{1, 2, 3}
"You typed 2 twice; the set keeps one. A set holds only unique values."
Turn a messy list into its unique values:
nums = [1, 2, 2, 3, 3, 3]
print(set(nums))
print(len(set(nums)))
Output (run-verified):
{1, 2, 3}
3
"set(nums) is the cleanest dedupe in Python; len(set(nums)) counts the distinct items. (How many unique visitors? len(set(visitor_ids)).)"
Membership — fast in:
s = {1, 2, 3}
print(2 in s)
print(5 in s)
Output (run-verified):
True
False
The headline warning — sets are UNORDERED (put it on a slide, big):
"A set has no positions — there is no s[0], and you must not rely on the order it prints in. For these tiny integer examples the order happens to look tidy, but that's luck, not a rule. The moment you need order, use a list. The thing a set guarantees is what's in it, never the order. This is exactly what chatbots forget — they'll confidently print a string set in 'the order you typed it.' Don't trust it; if order matters, don't use a set."
The empty-braces gotcha:
x = {}
print(type(x))
Output (run-verified):
<class 'dict'>
"{} is an empty dictionary, not an empty set — Python reserved {} for dicts. An empty set is set(). Worth knowing so it doesn't bite you."
Segment 7 — Choosing the Right Collection + Quick Interaction (20 min)
Land the headline skill — put this table on a slide and walk every row:
| Collection | Looks like | Ordered? | Changeable? | Reach for it when… |
|---|---|---|---|---|
| list | [1, 2, 3] |
yes (by index) | yes | you need an ordered sequence you'll add to, remove from, or reorder (scores, a playlist, a queue) |
| tuple | (1, 2) |
yes (by index) | no (immutable) | a fixed group that shouldn't change (a coordinate, a date, an RGB color) |
| dict | {"a": 1} |
keeps insertion order | yes | you look things up by a key/name (a phone book, word counts, settings, a price list) |
| set | {1, 2, 3} |
no | yes (add/remove) | you want only unique values, or fast membership tests (dedupe, "have I seen this ID?") |
"Two questions settle almost every choice: (1) Do I look things up by a name/label? → dictionary. (2) Do I just need the unique values? → set. Otherwise it's a list (changeable order) or a tuple (fixed)."
Interaction — "which collection?" rapid-fire (~6 min): put four jobs on a slide; students pick the collection solo (20 sec each), then discuss:
- "Store each student's grade by their name." → dict (look up by name)
- "Keep the unique tags used across all posts." → set (unique)
- "A GPS coordinate that won't change." → tuple (fixed pair)
- "A to-do list you reorder all day." → list (ordered + changeable)
Interaction — Predict-then-Run (~6 min): put four programs on a slide; students predict each output solo (30 sec), compare with a neighbor (1 min), then you run all four. Suggested with run-verified outputs:
print(len({3, 3, 4, 5, 5})) # 3
d = {"pen": 3}; d["pen"] = d["pen"] + 2; print(d["pen"]) # 5
caps = {"France": "Paris", "Japan": "Tokyo"}; print(caps["Japan"]) # Tokyo
print({2, 4, 4, 6}) # {2, 4, 6}
Tally how many the room got — celebrate any miss on the set items as "this is why we run code (and why we never trust a set's order)."
Segment 8 — Technology Workflow + AI-Critique, Callback & Hand-off (18 min) · Session 2 closes (~75)
Technology workflow — watch a dictionary fill up:
1. Open the free online Python environment (link in the readings). Build a dictionary, look up a key, run a set(...) dedupe; read what comes back.
2. To watch a dictionary grow, paste into Python Tutor and click forward:
python
counts = {}
for w in ["red", "blue", "red"]:
counts[w] = counts.get(w, 0) + 1
print(counts)
You'll see the dictionary box gain 'red': 1, then 'blue': 1, then watch 'red' tick up to 2. That growing key→value box is the lesson.
AI-critique moment (students verify, not consume):
Paste this to an approved chatbot: "In Python, what does each of these print? (a)
print({3, 1, 2, 1})(b)ages = {'Sam': 20}thenprint(ages['Ada'])(c)print({'x': 1, 'y': 2}.get('z', 0)). Give the exact output of each."
Then check by running all three yourself. Two traps: for (a), the model often prints the set in the order you typed it or "sorted" and states it confidently — a set is unordered, so don't trust the order it claims. For (b), it may invent a number or sayNone— it's actually aKeyError(a crash). Your job all semester: the tool drafts, you run it and judge. This week's blind spots are set ordering and pretending a missing key is fine.
Callback + tease:
- Callback: "Today: tuples (immutable — point[0] = 9 is a TypeError); dictionaries (look up by key, add/update, .get() to dodge the KeyError, iterate with .items()); sets (dedupe, membership, unordered); and the headline — choose the collection that fits the job."
- Tease next week: "We've spent two weeks on collections. Next week we go deep on the collection you've used since Day 1 without thinking about it: strings. You'll meet string methods (.upper(), .split(), .strip(), .replace()), learn to parse and build text, and write your first simple text-processing programs — and a string, it turns out, is a lot like an immutable sequence you already understand."
Hand-off (the week's graded work):
- Lecture Tutorial 10 (AI tutor, share-link submission) — tuples & immutability, dictionaries (lookup, .get(), KeyError), sets, and choosing a collection.
- Quiz 10, Discussion 10 ("Open Source vs. Proprietary: Should Code Be Free or Owned?"), and Assignment 10 ("Build a Lookup, Dedupe a List").
- Coding Lab 10 — "Map It, Dedupe It, Lock It" — build a dictionary and a set, run a predict-then-run table, fix a KeyError and a tuple-mutation bug, and watch a dictionary fill up in Python Tutor.
Instructor FAQ — Common Stumbles
| Student says / does | Quick cure |
|---|---|
| "I looked up a key and it crashed." | A missing key with d[key] raises KeyError. Use d.get(key) (returns None) or check if key in d first. |
Expects d["missing"] to return None. |
Square brackets crash; only .get() returns None. Run both side by side to feel the difference. |
Tries point[0] = 9 on a tuple. |
Tuples are immutable → TypeError: 'tuple' object does not support item assignment. Build a new tuple instead. |
| Thinks a set keeps the order you typed. | Sets are unordered — there's no s[0], and the print order isn't reliable. Need order? Use a list. |
Writes {} for an empty set. |
{} is an empty dict. An empty set is set(). |
| Uses a list as a dictionary key. | Keys must be immutable (strings, numbers, tuples) — a list can't be a key. (A tuple can.) |
| Confuses "key in dict" with "value in dict". | key in d checks keys, not values. That's almost always what you want. |
| "I'll trust the chatbot's printed set." | This week chatbots miss set ordering constantly and fake dict values. Run it and read what Python prints. |
Scope flag
This outline stays within Objective 6, the second collections week (tuples & immutability; dictionaries — lookup, add/update, .get(), .keys()/.values()/.items(), iteration, KeyError; sets — dedup, membership, unordered; choosing a collection). It uses only this-week and prior-week constructs — for loops (Week 6), if/else (Week 4), lists and indexing (Weeks 3, 9), and reading errors (Week 1). Strings in depth are Week 11 and only teased here; dict/set comprehensions, set algebra (|, &, -), del, defaultdict/Counter, nested dictionaries, and using tuples as dictionary keys beyond a passing mention are out of CS1 scope. Python and its type/method/exception names (dict, set, .get, TypeError, KeyError, …) are referenced factually; the instructor and institution remain fictional. Every output shown was produced by running the code.
~ Prof. Okafor's edition · Fall 2026 · built with thecoursemaker.com