From Working Code to Program Design · practice
Keep Question Data Together
The previous lesson put each question’s prompt, answer, and points into one
question = {
"prompt": "Which keyword starts a function?",
"answer": "def",
"points": 2,
}
That keeps the related values together. But another
Taking the question apart at every call
Imagine that score_attempt receives the answer and points as separate
def score_attempt(
answer,
response,
points,
):
...
Calling it requires several lookups from the same dictionary:
score_attempt(
question["answer"],
" DEF ",
question["points"],
)
Now imagine a quiz with several questions. The same pattern appears every time:
first_score = score_attempt(
first_question["answer"],
first_response,
first_question["points"],
)
second_score = score_attempt(
second_question["answer"],
second_response,
second_question["points"],
)
Each call takes one question apart and passes its pieces separately.
What happens when a rule is added?
Suppose some answers must now match uppercase and lowercase letters exactly. We add a case_sensitive
question = {
"prompt": "Which keyword starts a function?",
"answer": "def",
"points": 2,
"case_sensitive": False,
}
That one new rule causes several edits:
Add
case_sensitiveto each question dictionary.Add another
to score_attempt.Update every place that calls
score_attempt.Make sure each call passes the new value in the right position.
All these edits must agree. Missing just one can cause an error or give the wrong result.
Pass the complete question
The answer, points, and case rule all describe the same question. We can pass that question directly:
def score_attempt(question, response):
...
The calls become:
first_score = score_attempt(first_question, first_response)
second_score = score_attempt(second_question, second_response)
If the question later gains another scoring rule, score_attempt can read it from the dictionary. The calls already pass the complete question, so they may not need to change.
Try the same idea in this smaller example:
Add another key to question and run the example again. The call question_label(question) still passes one complete question.
This is not a rule that every function should receive a dictionary. Separate values are still appropriate when they represent separate ideas. Here, several values already belong to one question, and the function needs more than one of them.
The functions now have focused responsibilities, meaning the promises each part of the program owns. response_matches owns the comparison rule. score_attempt owns the decision to award this question’s points or zero. Passing the complete question lets each function do its own job without making every caller coordinate the fields.
Why can score_attempt(question, response) be easier to change later?
The exercise keeps the comparison code visible and complete. Your task is only to make score_attempt use the complete question and return the correct number of points.
Task
Complete score_attempt(question, response).
The provided response_matches case_sensitive rule.
Call
response_matcheswith the complete question and the response.If it returns
True, returnquestion["points"].Otherwise return
0.
Do not take the question apart into extra