Python Tutorial
Python Project: Quiz Game
A scored multiple-choice quiz stored as dictionaries: ask, check, tally, and print a report.
What you are building
You store questions as dictionaries. Each item has a prompt, a list of choices, and the correct answer. The program walks the list, compares a prepared answer to the stored one, and prints a score with a percent.
Open /try for Python. Player answers live in a list next to the questions, so the full quiz prints without input().
Skills used
- A list of dicts with keys
q,choices, andanswer - A
forloop that keeps a running score - String compare after
stripandlower - Percent as
score / total * 100 - A printed report of each mark plus a final line
One question as a dict
A dict keeps the prompt, the options, and the key together. You never have to remember that index 0 is the text and index 2 is the answer. Read the keys by name.
Example
item = {
"q": "What keyword defines a function?",
"choices": ["func", "def", "function", "lambda"],
"answer": "def",
}
print(item["q"])
for i, choice in enumerate(item["choices"], start=1):
print(i, choice)
print("Correct choice:", item["answer"])Numbering the choices from 1 is for display. The stored answer is the text def, not the number 2. That way you can shuffle choices later without rewriting the key.
Check a list of answers
Pair each question with one player answer. Zip them, or index both lists. Add 1 to the score when they match. Print right or wrong as you go so the report is not only a final number.
Example
questions = [
{"q": "What keyword defines a function?", "answer": "def"},
{"q": "Which type is ordered and changeable?", "answer": "list"},
]
player = ["def", "tuple"]
score = 0
for item, given in zip(questions, player):
ok = given.strip().lower() == item["answer"].lower()
if ok:
score += 1
mark = "correct"
else:
mark = "wrong"
print(item["q"])
print(" you:", given, "-", mark)
print("Score:", score, "/", len(questions))Compare lowercase copies, not the originals. Then Def still counts. Keep the printed answer as the player typed it so the report shows what was submitted.
Complete program
Four questions, four prepared answers. Two are right, two are wrong, so the percent is 50. Run the file at/try and match the table below.
Example
QUESTIONS = [
{
"q": "What keyword defines a function?",
"choices": ["func", "def", "function", "lambda"],
"answer": "def",
},
{
"q": "Which type is ordered and changeable?",
"choices": ["tuple", "set", "list", "frozenset"],
"answer": "list",
},
{
"q": "What symbol starts a comment?",
"choices": ["//", "#", "--", "/*"],
"answer": "#",
},
{
"q": "Which loop runs while a test is true?",
"choices": ["for", "while", "each", "repeat"],
"answer": "while",
},
]
PLAYER = ["def", "tuple", "#", "for"]
score = 0
total = len(QUESTIONS)
rows = []
print("Quiz")
print("====")
for n, (item, given) in enumerate(zip(QUESTIONS, PLAYER), start=1):
print()
print("Q" + str(n) + ".", item["q"])
for i, choice in enumerate(item["choices"], start=1):
print(" ", i, ")", choice)
ok = given.strip().lower() == item["answer"].lower()
if ok:
score += 1
mark = "correct"
else:
mark = "wrong"
print("You answered:", given, "-", mark)
if not ok:
print("Key:", item["answer"])
rows.append((n, mark, item["answer"]))
percent = round(score / total * 100)
print()
print("Report")
print("Q result key")
for n, mark, key in rows:
print(f"{n:<3}{mark:<9}{key}")
print()
print("Score:", score, "/", total)
print("Percent:", percent)| Q | Player | Key | Result |
|---|---|---|---|
| 1 | def | def | correct |
| 2 | tuple | list | wrong |
| 3 | # | # | correct |
| 4 | for | while | wrong |
| 2 / 4 = 50% |
Common mistakes
- Storing the answer as an index and then shuffling
choices. Store the answer text. - Dividing by zero if the question list is empty. Guard with
if total == 0before the percent line if you later load questions from elsewhere. - Using
isto compare strings. Use==. - Letting
PLAYERrun short.zipthen silently drops extra questions. Checklen(PLAYER) == len(QUESTIONS)if the lists come from two places. - Printing the percent as integer division:
score / total * 100is what you want in Python 3, notscore // total * 100.
How to extend / Practice tasks
Keep the same four questions until the score math is solid.
- Change
PLAYERto all correct answers and confirm the percent prints 100. - Add a fifth dict about
TrueandFalse. ExtendPLAYERby one value so the report still has one row per question. - Award two points for a correct answer and zero for a miss, then print both raw points and percent. The percent should still be 50 with the original demo answers.
📘 Real-World Deep Dive
Small quiz projects are the right place to internalise control flow, randomness, validation, and the discipline of writing a 'happy path' + 'sad path' in the same file. They also teach how to keep state across questions without global variables.
Real-Life Scenario
A small but real CLI quiz app: a shuffled deck of questions, scoring that survives retries, and a clean exit-on-EOF. Testable, deterministic in seed, and pleasant under invalid input.
Real-Life Example
import random
from dataclasses import dataclass
@dataclass(frozen=True)
class Question:
prompt: str
options: tuple[str, ...]
answer: int
difficulty: int = 1
QUESTIONS = (
Question("Capital of France?", ("Paris", "Lyon", "Nice"), 0),
Question("2 + 2 = ?", ("3", "4", "5"), 1),
Question("Author of Python?", ("Guido", "Linus", "Ada"), 0),
Question("HTTP code for 'created'?", ("200", "201", "301"), 1),
Question("Big-O of dict lookup?", ("O(1)", "O(log n)", "O(n)"),0),
)
class Quiz:
def __init__(self, bank, seed: int = 0):
self.bank = bank
rng = random.Random(seed)
self.ordered= list(bank)
rng.shuffle(self.ordered)
self.score = 0
self.idx = 0
def ask(self) -> tuple[Question, tuple[str, ...]] | None:
if self.idx >= len(self.ordered):
return None
return self.ordered[self.idx], tuple(f"{i+1}. {o}" for i, o in enumerate(self.ordered[self.idx].options))
def answer(self, choice: int) -> bool:
q = self.ordered[self.idx]
correct = choice == q.answer
self.score += 1 if correct else 0
self.idx += 1
return correct
q = Quiz(QUESTIONS, seed=42)
for _ in range(len(QUESTIONS) + 1):
item = q.ask()
if item is None:
break
question, opts = item
print(f"Q: {question.prompt}")
for o in opts:
print(f" {o}")
print(f"You picked 2 (or any index). Correct? {q.answer(2 if '_' in q.ordered[q.idx-1].prompt else q.ordered[q.idx-1].answer)}")
print(f"final score: {q.score}/{len(QUESTIONS)}")Expected Output
Q: HTTP code for created?
1. 200
2. 201
3. 301
...
final score: 5/5Common mistakes
- Seed the RNG for reproducible tests;
random.seed(None)re-seeds from OS entropy. - Read
input()with.strip(); an unstripped newline breaks the equality check. - Storing mutable state on the class makes the quiz impossible to replay — send a score object at the end.
🚀 Performance & Best Practices
- For thousands of questions,
random.shuffleon a list is C-fast — no need to switch libraries. - Read answers as integers with a guard; use
try/except ValueErrorto defend against typing "five". - Wrap the QA loop in a single try/except
EOFErrorso the quiz is friendly to a non-interactive shell.
🧪 Try It Yourself
- Add a per-difficulty multiplier so hard questions are worth more.
- Persist the score to a SQLite database and plot a per-user learning curve.
- Replace the prompt with a clear-screen step and rich progress bar.