Skip to Content
Course content

136: Practice Exercise: Building a Class-Based Quiz Game

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Before we dive into building the full quiz game, I want to show you a piece of code that looks perfectly fine on the surface but will drive you crazy once you actually run it. I see this mistake constantly, even from developers who have been using Python for a while. It's the classic "mutable default argument" trap.

class Quiz:
    def __init__(self, questions=[]): 
        self.questions = questions
        self.score = 0

# Let's create two different quizzes
math_quiz = Quiz()
math_quiz.questions.append({"q": "2+2", "a": "4"})

science_quiz = Quiz()
print(f"Science questions: {science_quiz.questions}") 
# Expected: []
# Actual: [{'q': '2+2', 'a': '4'}]

The Shared List Mystery

If you ran that code, you'd notice something unsettling: science_quiz somehow knows about the math question, even though we never added it to that specific instance. It feels like the two objects are haunting each other.

Here is what's happening: In Python, default arguments are evaluated once at the time the function (or method) is defined, not every time the function is called. Because a list is "mutable" (meaning it can be changed after it's created), that [] is actually a single list object stored in memory. Every time you create a Quiz instance without providing your own list, Python points self.questions to that same single list. You aren't creating a new empty list for every quiz; you're sharing one giant list across every instance of the class.

The None-Default Pattern

To fix this, we use a pattern that I've used in almost every production project I've ever written. We set the default value to None, and then we handle the list creation inside the __init__ method. Since the assignment happens inside the method body, it executes every time a new object is instantiated.

class Quiz:
    def __init__(self, questions=None): 
        if questions is None:
            self.questions = []
        else:
            self.questions = questions
        self.score = 0

# Now they stay separate
math_quiz = Quiz()
math_quiz.questions.append({"q": "2+2", "a": "4"})

science_quiz = Quiz()
print(f"Science questions: {science_quiz.questions}") 
# Actual: []

Structuring the Quiz Game Logic

Now that we've cleared that hurdle, let's talk about how to actually architect this. You don't want one giant class doing everything. That's a recipe for code that's impossible to test. Instead, I recommend a two-class approach:

  • The Question Class: This should be a simple "data container." It holds the prompt and the correct answer. It doesn't care about the score or the game loop; it just knows what a single question looks like.
  • The Quiz Class: This is the "engine." It manages the list of Question objects, tracks the user's current score, and handles the logic of iterating through the questions.

By separating these, you can easily change how a question is structured (maybe you want to add "hints" or "difficulty levels" later) without having to rewrite the entire game engine. I've always found that keeping data separate from logic makes the debugging process significantly faster.




📋 Practical Task

Building a Multi-Category Quiz Engine with Score Tracking

Your goal is to build a fully functional, class-based quiz system. Instead of using simple dictionaries, you will use an object-oriented approach to manage the data and the game state.

Requirements:

  • Create a Question class that takes a text string and an answer string during initialization.
  • Create a Quiz class that:
    • Initializes with a list of Question objects (use the None default pattern we discussed).
    • Has a score attribute that starts at 0.
    • Has a run_quiz() method that loops through the questions, asks the user for input, compares it to the correct answer (case-insensitive), and increments the score for correct answers.
    • Prints the final score as a percentage at the end.
  • Instantiate at least three Question objects and pass them into a Quiz instance, then call run_quiz() to play the game.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.