From Working Code to Program Design · practice
The Cost of Informal Dictionary Shapes
A
good = {
"prompt": "Two plus two?",
"answer": "4",
"points": 1,
}
missing_answer = {
"prompt": "Three plus three?",
"points": 1,
}
wrong_points = {
"prompt": "Four plus four?",
"answer": "8",
"points": -5,
}
The dictionaries are valid Python values. Only the first one is a valid quiz question according to our program’s needs.
That difference is important:
Python validates dictionary syntax.
Your program must validate the meaning and expected shape.
Create records through one function
A construction
def make_question(prompt, answer, points):
return {
"prompt": prompt,
"answer": answer,
"points": points,
}
The function becomes the normal path for creating questions. It can reject unsuitable values before returning a partially useful record.
Fundamentals I introduced raise as the way a function reports that it cannot produce a normal result. Here, the constructor function uses that same tool to protect the rules for a valid question:
if not prompt.strip():
raise ValueError("A question needs a prompt")
After .strip(), a blank prompt becomes the empty "". You learned in Fundamentals I that an empty string is falsy, so not makes the rejection
This improves the design while keeping familiar dictionaries. Every part of the program can receive question records with the same keys and validated values.
What does make_question improve?
This is a useful design on its own. One function now gives the program a consistent way to create question records.
Task
Complete make_question.
Reject a blank prompt, a blank answer, and points below 1 by raising prompt, answer, and points values supplied by the caller.