Layer 1 Session 05 Python Foundations

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.

name"Kenya" region"Africa" population54000000 key : value pairs

A container that groups related values under named keys.

Estimated time: 35โ€“40 minutes  ยท  Pre-quiz โ†’ Concept โ†’ Lab โ†’ Commit โ†’ Post-quiz

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.

Question 1 of 5

You have this code: country = {"name": "Kenya", "region": "Africa"}
What does country["region"] return?

Square-bracket lookup reads a value by its key. country["region"] looks up the key "region" and returns "Africa".
Question 2 of 5

You write a = {"x": 1} then b = a then b["x"] = 99.
What is a["x"] after this?

Dictionaries are mutable objects. 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.
Question 3 of 5

A key is stored in a variable: key = "population".
Which correctly reads that value from country?

Python dictionaries do not support dot notation at all โ€” country.key is not valid syntax for reading a dictionary value. country[key] evaluates the variable first, then looks up that key.
Question 4 of 5

What is the difference between country["capital"] and country.get("capital") when the key does not exist?

Bracket access is strict โ€” a missing key raises KeyError. .get() is forgiving โ€” it returns None by default, or a second argument you provide, e.g. country.get("capital", "Unknown").
Question 5 of 5

Which correctly adds a new key to an existing dictionary country?

Assigning to a bracket key that does not yet exist creates it. Dictionaries have no .append() โ€” that is a list method. Dot-assignment sets an attribute, not a dictionary key.

3. The Concept โ€” Python Dictionaries

3 VARIABLEScountry_namecountry_regioncountry_populationONE DICTname: "Kenya"region: "Africa"population: 54000000

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โ†’Bโ†’HEAP{"score": 99}

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

Lab objective: Create a single Python file that defines one country as a dictionary, reads its values two different ways, and prints the results.

What you will build

A file called country.py that demonstrates every dictionary concept from this session. Nothing more.

Step-by-step instructions

1

Create the file

Create a new file at country.py. It does not exist yet.

# country.py
2

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,
}
3

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"])
4

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", "๐Ÿณ"))
5

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"])
6

Add and delete a key

country["flag"] = "๐Ÿ‡ฐ๐Ÿ‡ช"
print("After add:", country["flag"])
del country["independent"]
print("After delete:", country)
7

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

FileActionWhy
country.py Created The only file for this session. Plain Python, no imports.
docs/sessions/session-05/index.html Created This session document.
If you find yourself editing any other file, stop. This session touches exactly 2 files.

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"
Do not commit until you can answer out loud: "What is the difference between bracket access and .get(), and when would I use each?"

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.

Question 1 of 5

You have this code: country = {"name": "Kenya", "region": "Africa"}
What does country["region"] return?

Square-bracket lookup reads a value by its key. country["region"] looks up the key "region" and returns "Africa".
Question 2 of 5

You write a = {"x": 1} then b = a then b["x"] = 99.
What is a["x"] after this?

Dictionaries are mutable objects. 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.
Question 3 of 5

A key is stored in a variable: key = "population".
Which correctly reads that value from country?

Python dictionaries do not support dot notation at all โ€” country.key is not valid syntax for reading a dictionary value. country[key] evaluates the variable first, then looks up that key.
Question 4 of 5

What is the difference between country["capital"] and country.get("capital") when the key does not exist?

Bracket access is strict โ€” a missing key raises KeyError. .get() is forgiving โ€” it returns None by default, or a second argument you provide, e.g. country.get("capital", "Unknown").
Question 5 of 5

Which correctly adds a new key to an existing dictionary country?

Assigning to a bracket key that does not yet exist creates it. Dictionaries have no .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.

  1. You've used key-value structures before (JSON, hash maps, objects). What was different or surprising about Python's reference behaviour?
  2. 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?
  3. In Step 4, .get() returned None instead of crashing. When would you actually prefer the crash?
  4. 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.name later.
  • 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.