-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
136: Practice Exercise: Building a Class-Based Quiz Game
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
Questionobjects, 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
Questionclass that takes atextstring and ananswerstring during initialization. - Create a
Quizclass that:- Initializes with a list of
Questionobjects (use theNonedefault pattern we discussed). - Has a
scoreattribute 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.
- Initializes with a list of
- Instantiate at least three
Questionobjects and pass them into aQuizinstance, then callrun_quiz()to play the game.
There are no comments for now.