Dictionaries
Before we can model a country, a class, or any structured data in Python, we must deeply understand dictionaries โ the container every later session builds on.
A container that groups related values under named keys.
1. Learning Objective
By the end of this session you will be able to:
- Create a dictionary using literal syntax
- Read values using key lookup and the .get() method
- Add, update, and delete key-value pairs
- Explain why dictionaries are mutable and what that means when two names point at the same one
- Describe why Python classes store their data in a dictionary-like structure
2. Pre-Coding Quiz
Answer these before reading the concept explanation or writing any code. It is fine to get these wrong โ that is the point. You need 4/5 to proceed to the lab.
You have this code: country = {"name": "Kenya", "region": "Africa"}
What does country["region"] return?
country["region"] looks up the key "region" and returns "Africa".You write a = {"x": 1} then b = a then b["x"] = 99.
What is a["x"] after this?
b = a does not copy the dictionary โ it copies the reference. Both names point to the same object in memory, so mutating through b is visible through a.A key is stored in a variable: key = "population".
Which correctly reads that value from country?
country.key is not valid syntax for reading a dictionary value. country[key] evaluates the variable first, then looks up that key.What is the difference between country["capital"] and country.get("capital") when the key does not exist?
KeyError. .get() is forgiving โ it returns None by default, or a second argument you provide, e.g. country.get("capital", "Unknown").Which correctly adds a new key to an existing dictionary country?
.append() โ that is a list method. Dot-assignment sets an attribute, not a dictionary key.3. The Concept โ Python Dictionaries
Three loose variables collapse into one dict โ each entry is a key : value pair.
What is a dictionary?
A dictionary groups related data together under one name, using key-value pairs. Instead of three separate variables for a country's data, you have one container that holds all of them.
# Without a dictionary โ three unrelated variables
country_name = "Kenya"
country_region = "Africa"
country_population = 54000000
# With a dictionary โ one container, logically grouped
country = {
"name": "Kenya",
"region": "Africa",
"population": 54000000,
}
Reading values โ bracket access vs .get()
There are two ways to read a value from a dictionary, and they behave differently on a missing key.
country = {"name": "Kenya", "region": "Africa"}
print(country["name"]) # "Kenya"
print(country["region"]) # "Africa"
# Missing key with bracket access -> KeyError, program crashes
# print(country["capital"])
# Missing key with .get() -> None, no crash
print(country.get("capital")) # None
print(country.get("capital", "Unknown")) # "Unknown" โ explicit default
Dictionaries are mutable โ this is critical
In Python there are mutable types (dict, list, set) and immutable types (str, int, float, bool, tuple). Immutable values are copied when assigned. Mutable objects are not copied โ only the reference is copied.
# Immutable โ a new value is bound, the old one is untouched
x = 5
y = x
y = 99
print(x) # still 5
# Mutable โ reference is shared, NOT copied
a = {"score": 5}
b = a # b now refers to the SAME dict as a
b["score"] = 99
print(a["score"]) # 99 โ because a and b are the same object
a and b are two names pointing at one dict in memory โ mutating through either name is visible through both.
Adding, updating, and deleting keys
Dictionaries grow and shrink after creation. Adding an unused key creates it; assigning an existing key overwrites it.
country = {"name": "Kenya"}
# Add a new key
country["capital"] = "Nairobi"
print(country) # {'name': 'Kenya', 'capital': 'Nairobi'}
# Update an existing key
country["name"] = "Kenya (updated)"
# Delete a key
del country["capital"]
print(country) # {'name': 'Kenya (updated)'}
Nested dictionaries
Values can themselves be dictionaries โ this is how you model structured, hierarchical data.
country = {
"name": "Kenya",
"location": {
"continent": "Africa",
"coordinates": {"lat": -0.0236, "lng": 37.9062},
},
}
print(country["location"]["continent"]) # "Africa"
print(country["location"]["coordinates"]["lat"]) # -0.0236
We are not writing classes yet, but when we do in Session 12, you will recognise this pattern immediately: an object's internal attributes are stored and looked up the same way you just looked up dictionary keys.
4. Lab
What you will build
A file called country.py that demonstrates every dictionary concept from this session. Nothing more.
Step-by-step instructions
Create the file
Create a new file at country.py. It does not exist yet.
# country.py
Define your country dictionary
Write a variable called country. Give it at least 5 keys. Use real values โ pick any country you like.
country = {
"name": "Kenya",
"capital": "Nairobi",
"region": "Africa",
"population": 54000000,
"independent": True,
}
Read values with bracket access
Print three of the values using bracket access.
print("Name (bracket):", country["name"])
print("Capital (bracket):", country["capital"])
print("Region (bracket):", country["region"])
Read a value with .get(), including a missing key
Prove that .get() does not crash on a missing key, while bracket access would.
print("Flag (.get, missing):", country.get("flag"))
print("Flag with default:", country.get("flag", "๐ณ"))
Prove the reference behaviour
Before running: write down what you think original["score"] will be. Then run it.
original = {"name": "Kenya", "score": 0}
copy = original # This is NOT a copy
copy["score"] = 99
print("original[score]:", original["score"]) # What do you expect?
print("copy[score]:", copy["score"])
Add and delete a key
country["flag"] = "๐ฐ๐ช"
print("After add:", country["flag"])
del country["independent"]
print("After delete:", country)
Add a nested dictionary
Access a deeply nested value.
country["location"] = {
"continent": "Africa",
"coordinates": {"lat": -0.0236, "lng": 37.9062},
}
print("Continent:", country["location"]["continent"])
print("Latitude:", country["location"]["coordinates"]["lat"])
5. Expected Files Changed
| File | Action | Why |
|---|---|---|
country.py |
Created | The only file for this session. Plain Python, no imports. |
docs/sessions/session-05/index.html |
Created | This session document. |
6. Commit Checkpoint
Once the lab is complete and you can explain every line, make this exact commit:
git add country.py docs/sessions/session-05/index.html
git commit -m "session-05: define country as a dict, explore lookup and reference behaviour"
7. Code Review Checklist
Go through your code line by line and check each item:
8. Post-Coding Quiz
Same 5 questions. Take it again now that you have written and run the code. You need 4/5 to mark this session complete.
You have this code: country = {"name": "Kenya", "region": "Africa"}
What does country["region"] return?
country["region"] looks up the key "region" and returns "Africa".You write a = {"x": 1} then b = a then b["x"] = 99.
What is a["x"] after this?
b = a does not copy the dictionary โ it copies the reference. Both names point to the same object in memory, so mutating through b is visible through a.A key is stored in a variable: key = "population".
Which correctly reads that value from country?
country.key is not valid syntax for reading a dictionary value. country[key] evaluates the variable first, then looks up that key.What is the difference between country["capital"] and country.get("capital") when the key does not exist?
KeyError. .get() is forgiving โ it returns None by default, or a second argument you provide, e.g. country.get("capital", "Unknown").Which correctly adds a new key to an existing dictionary country?
.append() โ that is a list method. Dot-assignment sets an attribute, not a dictionary key.9. Reflection Questions
Think through these after the post-quiz. No right answer โ they are for discussion.
- You've used key-value structures before (JSON, hash maps, objects). What was different or surprising about Python's reference behaviour?
- If dictionaries are shared by reference, what real bug could happen if you passed a dictionary into a function and the function modified it without you expecting that?
- In Step 4,
.get()returnedNoneinstead of crashing. When would you actually prefer the crash? - Can you think of a realistic program where a dictionary key would need to be looked up dynamically from a variable?
10. What Breaks If This Knowledge Is Missing?
- Silent state bugs: If you believe dictionaries are copied when assigned, you will be confused when a function's changes to a dictionary affect the caller's copy too. This is the single most common source of "spooky action at a distance" bugs in Python.
- Class attributes (Layer 2): In Session 12, you will learn that every Python object stores its attributes in a dictionary-like structure internally. Understanding key-value lookup here is the entire foundation for understanding
self.namelater. - JSON and APIs (Layer 4 and 7): Every JSON API response you will parse in Session 42 becomes a Python dictionary. Missing this session means you cannot read real API data.
11. What We Learned
Python concept mastered: Dictionaries โ key-value containers, bracket access vs .get(), mutability, reference semantics, nesting.
Unlocks: You now understand the data structure Python uses everywhere: function keyword arguments, JSON, and (soon) object attributes.
Next session: Session 06 โ Lists and Iteration. We will store multiple countries and iterate over them โ the pattern every later data-processing session builds on.