Questions
What problem does Dictionaries help us solve in a small Python program?
What should we predict before running the example?
What value, output, or error should we inspect after changing one line?
Objectives
Run a complete example for dictionaries in Colab.
Explain the example line by line using plain language.
Change one part of the code and predict the result before running it.
Recognize one common mistake and use the error message as evidence.
Hands-on episode: Dictionaries
A dictionary stores key-value pairs. Keys must be unique and are used for lookup; values can be any information you need to store.
We will learn this by running code, not by memorizing a definition first. Open the Colab notebook from the button above, find this section, and run each cell in order. Keep a small note beside the notebook with three columns: prediction , actual result , and what changed .
Example 1.1
Predict what happens when the same dictionary is read, updated, and safely queried.
student = {"name" : "Ada" , "score" : 8 }
student["score" ] = student["score" ] + 1
print (student["name" ])
print (student.get("city" , "unknown" ))
Run the cell once without editing it. If the result is different from your prediction, leave the prediction visible and write one sentence about the difference. That sentence is more useful than a perfect first guess.
Explain Example 1.1
The dictionary starts with two keys: name and score.
The update line looks up the old score, adds one, and stores the new score under the same key.
student["name"] requires the key to exist and returns Ada.
.get("city", "unknown") avoids a KeyError by returning a default when the key is missing.
Now explain the example out loud or in a Markdown cell. Use short sentences: “this line creates…”, “this name stores…”, “this output appears because…”. If you cannot explain a line yet, run only the lines above it and inspect the values that exist at that moment.
Challenge 1.1
Replace .get("city", "unknown") with student["city"]. The error is not random; Python is saying that key does not exist in the dictionary at that moment.
Show a safe way to approach the challenge
Copy Example 1.1 into a new Colab cell.
Change exactly one value, name, condition, or line.
Write the expected output before running the cell.
Run the cell and compare the actual result with your prediction.
If the result surprises you, undo the change and try a smaller one.
Suggested first move: Replace .get("city", "unknown") with student["city"].
Debugging checkpoint 1.1
Beginners often use a list when they really need lookup by name. If you keep asking ‘where is the item with this label?’, a dictionary is usually clearer. When a KeyError appears, print student.keys() or check 'city' in student before reading the value.
Do not debug by rewriting the whole example. Read the error type or surprising output, inspect the closest value with print(...) or type(...), then change one thing. This is the same routine you will use in larger projects.
Apply it
In Colab, build a profile dictionary with name, city, and favorite topic. Update one value, add one new key, loop through .items(), and print a friendly summary.
Finish by adding a Markdown cell that answers: What did this example teach me that I can reuse in a project?
Key points
Learn the concept by running a complete, small example first.
Predict before execution so your thinking becomes visible.
Change one thing at a time so cause and effect stay clear.
Treat errors as clues about the exact line or value Python could not handle.
The problem this lesson solves
Lists are good when position matters: first item, second item, third item. But many programs need lookup by a meaningful label: a person’s name, a product ID, a column name, or a setting. A dictionary stores key-value pairs so you can ask for a value by key.
A dictionary is useful when your question sounds like:
“What is Ada’s score?”
“How many times have we seen this word?”
“What email address belongs to this user?”
“What settings did the user choose?”
How dictionary lookup works
A dictionary is a lookup table. You provide a key; Python returns the value stored under that key.
flowchart LR
key1["key<br/>'name'"] --> value1["value<br/>'Ada'"]
key2["key<br/>'role'"] --> value2["value<br/>'programmer'"]
key3["key<br/>'language'"] --> value3["value<br/>'Python'"]
lookup["person['name']"] --> key1
value1 --> result["result<br/>'Ada'"]
When a KeyError appears, compare the key you asked for with the keys actually inside the dictionary.
1. Key-value pairs
A dictionary is written with curly braces. Each pair has a key, a colon, and a value.
person = {"name" : "Ada" , "role" : "programmer" }
Read this as: the key "name" maps to the value "Ada"; the key "role" maps to the value "programmer".
Example 1
Predict the two printed outputs before running the code.
person = {"name" : "Ada" , "role" : "programmer" }
print (person["name" ])
person["language" ] = "Python"
print (person)
Step-by-step explanation
person = {"name": "Ada", "role": "programmer"}
Python creates a dictionary with two key-value pairs.
person["name"]
Python looks for the key "name".
It returns the value "Ada".
person["language"] = "Python"
This adds a new key-value pair.
If the key already existed, it would replace the old value.
print(person) displays the dictionary.
Keys must be unique
A dictionary cannot keep two different values for the same key:
scores = {"Ada" : 90 , "Ada" : 95 }
print (scores)
The later value wins, so the dictionary contains only {"Ada": 95}.
Common mistakes
Mistake: asking for a key that is not there.
person = {"name" : "Ada" }
print (person["email" ])
This raises KeyError. Python is telling you: “I looked for this key and did not find it.”
Check your understanding
{
const scripts = Array . from (
document . querySelectorAll ("script.fcpython-ojs-quiz-config" )
);
const script = scripts. find (
(node) => node. dataset . fcpythonRendered !== "true"
);
if (! script) {
return html `<div class="fcpython-quiz fcpython-quiz-warning">
Quiz configuration was not found.
</div>` ;
}
script. dataset . fcpythonRendered = "true" ;
const quiz = JSON . parse (script. textContent );
const container = html `<div class="fcpython-quiz"></div>` ;
const title = document . createElement ("h3" );
title. textContent = quiz. title ;
container. appendChild (title);
const instructions = document . createElement ("p" );
instructions. textContent = quiz. instructions ;
container. appendChild (instructions);
const feedbackNodes = [];
quiz. questions . forEach ((question, questionIndex) => {
const fieldset = document . createElement ("fieldset" );
fieldset. className = "fcpython-quiz-question" ;
const legend = document . createElement ("legend" );
legend. textContent = ` ${ questionIndex + 1 } . ${ question. prompt } ` ;
fieldset. appendChild (legend);
question. options . forEach ((option, optionIndex) => {
const label = document . createElement ("label" );
label. className = "fcpython-quiz-option" ;
const input = document . createElement ("input" );
input. type = "radio" ;
input. name = ` ${ quiz. id } - ${ question. id } ` ;
input. value = String (optionIndex);
const text = document . createElement ("span" );
text. textContent = option;
label. appendChild (input);
label. appendChild (text);
fieldset. appendChild (label);
});
const feedback = document . createElement ("p" );
feedback. className = "fcpython-quiz-feedback" ;
feedback. setAttribute ("aria-live" , "polite" );
feedbackNodes. push (feedback);
fieldset. appendChild (feedback);
container. appendChild (fieldset);
});
const actions = document . createElement ("div" );
actions. className = "fcpython-quiz-actions" ;
const check = document . createElement ("button" );
check. type = "button" ;
check. textContent = "Check answers" ;
const reset = document . createElement ("button" );
reset. type = "button" ;
reset. textContent = "Reset" ;
const score = document . createElement ("p" );
score. className = "fcpython-quiz-score" ;
score. setAttribute ("aria-live" , "polite" );
check. addEventListener ("click" , () => {
let correctCount = 0 ;
quiz. questions . forEach ((question, questionIndex) => {
const selected = container. querySelector (
`input[name=" ${ quiz. id } - ${ question. id } "]:checked`
);
const feedback = feedbackNodes[questionIndex];
if (! selected) {
feedback. textContent = "Choose an answer before checking." ;
feedback. className = "fcpython-quiz-feedback" ;
return ;
}
const selectedIndex = Number (selected. value );
if (selectedIndex === question. answer_index ) {
correctCount += 1 ;
feedback. textContent = `✅ Correct. ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-correct" ;
} else {
const answer = question. options [question. answer_index ];
feedback. textContent = `❌ Not yet. Correct answer: ${ answer} . ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-incorrect" ;
}
});
score. textContent = `Score: ${ correctCount} / ${ quiz. questions . length } ` ;
});
reset. addEventListener ("click" , () => {
container. querySelectorAll ("input[type='radio']" ). forEach ((input) => {
input. checked = false ;
});
feedbackNodes. forEach ((feedback) => {
feedback. textContent = "" ;
feedback. className = "fcpython-quiz-feedback" ;
});
score. textContent = "" ;
});
actions. appendChild (check);
actions. appendChild (reset);
container. appendChild (actions);
container. appendChild (score);
return container;
}
2. Safe lookup and dictionary loops
When a key might be missing, check before direct lookup.
person = {"name" : "Ada" }
if "email" in person:
print (person["email" ])
else :
print ("No email saved yet." )
You can also use .get() to provide a default value:
email = person.get("email" , "No email saved yet." )
print (email)
Looping through a dictionary
scores = {"Ada" : 95 , "Grace" : 98 }
for name, score in scores.items():
print (f" { name} : { score} " )
.items() gives key-value pairs. In this example, each pair is unpacked into name and score.
Other common loops:
for name in scores:
print (name) # keys
for score in scores.values():
print (score) # values
for name, score in scores.items():
print (name, score) # key and value together
Check your understanding
{
const scripts = Array . from (
document . querySelectorAll ("script.fcpython-ojs-quiz-config" )
);
const script = scripts. find (
(node) => node. dataset . fcpythonRendered !== "true"
);
if (! script) {
return html `<div class="fcpython-quiz fcpython-quiz-warning">
Quiz configuration was not found.
</div>` ;
}
script. dataset . fcpythonRendered = "true" ;
const quiz = JSON . parse (script. textContent );
const container = html `<div class="fcpython-quiz"></div>` ;
const title = document . createElement ("h3" );
title. textContent = quiz. title ;
container. appendChild (title);
const instructions = document . createElement ("p" );
instructions. textContent = quiz. instructions ;
container. appendChild (instructions);
const feedbackNodes = [];
quiz. questions . forEach ((question, questionIndex) => {
const fieldset = document . createElement ("fieldset" );
fieldset. className = "fcpython-quiz-question" ;
const legend = document . createElement ("legend" );
legend. textContent = ` ${ questionIndex + 1 } . ${ question. prompt } ` ;
fieldset. appendChild (legend);
question. options . forEach ((option, optionIndex) => {
const label = document . createElement ("label" );
label. className = "fcpython-quiz-option" ;
const input = document . createElement ("input" );
input. type = "radio" ;
input. name = ` ${ quiz. id } - ${ question. id } ` ;
input. value = String (optionIndex);
const text = document . createElement ("span" );
text. textContent = option;
label. appendChild (input);
label. appendChild (text);
fieldset. appendChild (label);
});
const feedback = document . createElement ("p" );
feedback. className = "fcpython-quiz-feedback" ;
feedback. setAttribute ("aria-live" , "polite" );
feedbackNodes. push (feedback);
fieldset. appendChild (feedback);
container. appendChild (fieldset);
});
const actions = document . createElement ("div" );
actions. className = "fcpython-quiz-actions" ;
const check = document . createElement ("button" );
check. type = "button" ;
check. textContent = "Check answers" ;
const reset = document . createElement ("button" );
reset. type = "button" ;
reset. textContent = "Reset" ;
const score = document . createElement ("p" );
score. className = "fcpython-quiz-score" ;
score. setAttribute ("aria-live" , "polite" );
check. addEventListener ("click" , () => {
let correctCount = 0 ;
quiz. questions . forEach ((question, questionIndex) => {
const selected = container. querySelector (
`input[name=" ${ quiz. id } - ${ question. id } "]:checked`
);
const feedback = feedbackNodes[questionIndex];
if (! selected) {
feedback. textContent = "Choose an answer before checking." ;
feedback. className = "fcpython-quiz-feedback" ;
return ;
}
const selectedIndex = Number (selected. value );
if (selectedIndex === question. answer_index ) {
correctCount += 1 ;
feedback. textContent = `✅ Correct. ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-correct" ;
} else {
const answer = question. options [question. answer_index ];
feedback. textContent = `❌ Not yet. Correct answer: ${ answer} . ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-incorrect" ;
}
});
score. textContent = `Score: ${ correctCount} / ${ quiz. questions . length } ` ;
});
reset. addEventListener ("click" , () => {
container. querySelectorAll ("input[type='radio']" ). forEach ((input) => {
input. checked = false ;
});
feedbackNodes. forEach ((feedback) => {
feedback. textContent = "" ;
feedback. className = "fcpython-quiz-feedback" ;
});
score. textContent = "" ;
});
actions. appendChild (check);
actions. appendChild (reset);
container. appendChild (actions);
container. appendChild (score);
return container;
}
3. Counting, grouping, and records
Dictionaries shine when you update values by key.
Counting words
words = ["red" , "blue" , "red" , "green" , "blue" , "red" ]
counts = {}
for word in words:
if word not in counts:
counts[word] = 0
counts[word] = counts[word] + 1
print (counts)
Step-by-step explanation
counts = {} starts with an empty dictionary.
The loop visits one word at a time.
If a word is not yet a key, the code creates it with count 0.
Then the count increases by 1.
The final result is {"red": 3, "blue": 2, "green": 1}.
Using dictionaries as records
A dictionary can represent one record:
student = {
"name" : "Ada" ,
"score" : 95 ,
"passed" : True ,
}
print (f" { student['name' ]} scored { student['score' ]} ." )
This is common in data science because one row of data often has named fields.
Common mistakes
Mistake: trying to update a count before the key exists.
counts = {}
counts["red" ] = counts["red" ] + 1
The lookup counts["red"] fails because the key has not been created yet. Check with if "red" not in counts, or use .get():
counts["red" ] = counts.get("red" , 0 ) + 1
Practice
In Colab, count letters in a short word:
word = "banana"
counts = {}
for letter in word:
counts[letter] = counts.get(letter, 0 ) + 1
print (counts)
Before running, predict which letter has the largest count.
Check your understanding
{
const scripts = Array . from (
document . querySelectorAll ("script.fcpython-ojs-quiz-config" )
);
const script = scripts. find (
(node) => node. dataset . fcpythonRendered !== "true"
);
if (! script) {
return html `<div class="fcpython-quiz fcpython-quiz-warning">
Quiz configuration was not found.
</div>` ;
}
script. dataset . fcpythonRendered = "true" ;
const quiz = JSON . parse (script. textContent );
const container = html `<div class="fcpython-quiz"></div>` ;
const title = document . createElement ("h3" );
title. textContent = quiz. title ;
container. appendChild (title);
const instructions = document . createElement ("p" );
instructions. textContent = quiz. instructions ;
container. appendChild (instructions);
const feedbackNodes = [];
quiz. questions . forEach ((question, questionIndex) => {
const fieldset = document . createElement ("fieldset" );
fieldset. className = "fcpython-quiz-question" ;
const legend = document . createElement ("legend" );
legend. textContent = ` ${ questionIndex + 1 } . ${ question. prompt } ` ;
fieldset. appendChild (legend);
question. options . forEach ((option, optionIndex) => {
const label = document . createElement ("label" );
label. className = "fcpython-quiz-option" ;
const input = document . createElement ("input" );
input. type = "radio" ;
input. name = ` ${ quiz. id } - ${ question. id } ` ;
input. value = String (optionIndex);
const text = document . createElement ("span" );
text. textContent = option;
label. appendChild (input);
label. appendChild (text);
fieldset. appendChild (label);
});
const feedback = document . createElement ("p" );
feedback. className = "fcpython-quiz-feedback" ;
feedback. setAttribute ("aria-live" , "polite" );
feedbackNodes. push (feedback);
fieldset. appendChild (feedback);
container. appendChild (fieldset);
});
const actions = document . createElement ("div" );
actions. className = "fcpython-quiz-actions" ;
const check = document . createElement ("button" );
check. type = "button" ;
check. textContent = "Check answers" ;
const reset = document . createElement ("button" );
reset. type = "button" ;
reset. textContent = "Reset" ;
const score = document . createElement ("p" );
score. className = "fcpython-quiz-score" ;
score. setAttribute ("aria-live" , "polite" );
check. addEventListener ("click" , () => {
let correctCount = 0 ;
quiz. questions . forEach ((question, questionIndex) => {
const selected = container. querySelector (
`input[name=" ${ quiz. id } - ${ question. id } "]:checked`
);
const feedback = feedbackNodes[questionIndex];
if (! selected) {
feedback. textContent = "Choose an answer before checking." ;
feedback. className = "fcpython-quiz-feedback" ;
return ;
}
const selectedIndex = Number (selected. value );
if (selectedIndex === question. answer_index ) {
correctCount += 1 ;
feedback. textContent = `✅ Correct. ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-correct" ;
} else {
const answer = question. options [question. answer_index ];
feedback. textContent = `❌ Not yet. Correct answer: ${ answer} . ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-incorrect" ;
}
});
score. textContent = `Score: ${ correctCount} / ${ quiz. questions . length } ` ;
});
reset. addEventListener ("click" , () => {
container. querySelectorAll ("input[type='radio']" ). forEach ((input) => {
input. checked = false ;
});
feedbackNodes. forEach ((feedback) => {
feedback. textContent = "" ;
feedback. className = "fcpython-quiz-feedback" ;
});
score. textContent = "" ;
});
actions. appendChild (check);
actions. appendChild (reset);
container. appendChild (actions);
container. appendChild (score);
return container;
}
Key points
Dictionaries map keys to values.
Keys are used for lookup and must be unique.
A missing key raises KeyError when accessed with square brackets.
Use in or .get() when a key may be absent.
.items() is useful when you need both key and value in a loop.
Dictionaries are useful for records, counters, grouping, and settings.
References
Python Tutorial: dictionaries: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
Python standard types: mapping types: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
Back to top