Today's outcomes
- Choose dictionary keys and access values safely.
- Use keys, values, and items.
- Model a record with explicit field meanings.
Before you start: Complete Day 09 and its practical challenge.
Understand the concept
A dictionary connects unique keys to values
A dictionary is a mapping. It connects a key such as "minutes" to a value such as 45. Keys must be hashable and unique within the mapping. Values may repeat and may be mutable. Strings are common record-field keys, while integers can be useful stable identifiers.
Choose a dictionary when a named relationship matters more than a position. session['minutes'] communicates a field's meaning more clearly than session[2] in a wide positional record. The dictionary itself does not enforce which fields exist, their types, or their units. Those requirements still belong to your contract.
Python dictionaries preserve insertion order, but they do not sort keys automatically. Updating an existing key changes its value without creating a second copy of the key. A mapping therefore cannot directly preserve two independent records under one identical key; Day 15 will examine that collision problem.
Missing keys and missing values are different
record['minutes'] raises KeyError when the key is absent. record.get('minutes') instead returns None by default for an absent key. That convenience can collapse two distinct states: an absent field and an explicitly recorded None. Use a membership check or a dedicated sentinel if those states need different handling.
Zero minutes is a valid known quantity in today's fixture. The condition if not minutes would combine zero with None, empty text, and other false-valued inputs. Use the test that matches the question. minutes is None asks about the chosen missing marker; 'minutes' in record asks whether the field exists.
Views let you iterate deliberately
keys gives a view of keys, values gives a view of values, and items gives key-value pairs. Iterating the dictionary itself also yields keys. Views reflect changes to the mapping rather than being independent historical snapshots. Build a list when you need an explicit snapshot.
Avoid adding or removing dictionary entries while iterating its live view. Even when only changing values is possible, a separate output mapping can make the transformation easier to inspect. Name the operation: are you updating one record, transforming every value, or grouping several records?
Worked example
session = {"id": "S1", "topic": "functions", "minutes": 45, "score": None}
print("Topic:", session["topic"])
print("Minutes:", session.get("minutes"))
print("Score missing:", session["score"] is None)
session["minutes"] = 60
session["completed"] = True
for field, value in session.items():
print(field, value)
assert session["minutes"] == 60
assert "completed" in session
assert "notes" not in session
assert session.get("notes", "not supplied") == "not supplied"
| Operation | Question answered | Result |
|---|---|---|
| session['topic'] | Required field's value | functions |
| session['score'] is None | Is this field explicitly missing? | True |
| 'notes' in session | Does the field exist? | False |
| get with explicit default | Optional field or fallback | not supplied |
The key order for printed fields follows insertion order in this script. That is useful for readability, but your calculation should not depend on a dictionary happening to print fields in a particular position.
Guided practice
- Predict: create one record with
minutes: 0, one withminutes: None, and one without the minutes key. Which tests distinguish all three? - Modify: add a notes field and remove it with pop. Capture the returned value. Explain the difference between removing a field and assigning None to that field.
- Reconstruct: represent two named sessions as a list of dictionaries. Loop through the list and print their IDs and known minute values. Do not invent zero minutes for a missing field.
A shallow dictionary copy protects the outer mapping from a direct field update, but nested lists may remain shared, just as in Day 8. Inspect both representation layers when a copied record changes unexpectedly.
Daily challenge
Implement summarize_records(records). Each input item is a dictionary. Distinguish absent minutes fields from explicitly missing None values. Nonmissing minutes are assumed to be nonnegative integers. Return a dictionary containing recorded_count, missing_count, absent_count, and total_minutes. Leave the input records unchanged.
| Record | Category | Contribution |
|---|---|---|
{'minutes': 30} |
recorded | 30 minutes |
{'minutes': 0} |
recorded | 0 minutes |
{'minutes': None} |
missing | No numeric contribution |
{'topic': 'lists'} |
absent | Missing field rather than missing observation |
For those four records, expect recorded 2, missing 1, absent 1, and total 30. For an empty list, expect all counts and total to be zero. The counts must add up to the input record count.
Reference solution
Compare after attempting the challenge
def summarize_records(records):
report = {"recorded_count": 0, "missing_count": 0,
"absent_count": 0, "total_minutes": 0}
for record in records:
if "minutes" not in record:
report["absent_count"] += 1
elif record["minutes"] is None:
report["missing_count"] += 1
else:
report["recorded_count"] += 1
report["total_minutes"] += record["minutes"]
return report
records = [{"minutes": 30}, {"minutes": 0}, {"minutes": None}, {"topic": "lists"}]
assert summarize_records(records) == {
"recorded_count": 2, "missing_count": 1,
"absent_count": 1, "total_minutes": 30}
assert summarize_records([])["recorded_count"] == 0
assert records[1]["minutes"] == 0
assert "minutes" not in records[3]
print(summarize_records(records))
The order of the first two branches preserves the distinction between an absent key and an explicit None. No exception handler is needed because membership is checked before required access.
Debug and explain
If KeyError appears, inspect which field the contract requires and which record lacks it. If a total excludes zeros, inspect truth-value checks. If a record disappears while building a mapping, inspect repeated keys rather than assuming the loop skipped an item.
Explain why get alone cannot distinguish the absent and explicit-None cases with its default. Then explain why a dictionary's unique keys do not imply that every input record has a unique identity. Identity must come from a suitable field and a stated uniqueness rule.
Review and next step
Save the four-record fixture and its accounting result. At the next review, rebuild the missing-field branches before the total. Day 11 compares collections through sets while preserving the distinction between membership and repeated observations.
Sources and verification
Original teaching examples checked with Python 3.12.14. Library behavior reviewed against these primary references on October 3, 2026. Day 26's examples also use Beautiful Soup 4.14.3.
Check your understanding
Five questions, 20 points each. Pass at 85% or above, which requires all five correct. Feedback explains each answer; revise the lesson and retry when needed.
Review your practical work
Check these statements after running your own challenge. This is your self-review; the website does not execute or assess your Python code.
Pass the knowledge check and finish the practical self-review to save completion.