Organizing a Program Across Files · practice
Import Classes Clearly
models.py holds copies of Question, Quiz, and Attempt. main.py still defines the originals. Finish the move by replacing those duplicate definitions with imports.
Connecting it takes one line, and which line you choose is a small design decision you will make hundreds of times.
Two ways to reach a name
With that class in a models, the same code can reach it two ways:
import models
question = models.Question("Which keyword starts a function?", "def", 2)
from models import Question
question = Question("Which keyword starts a function?", "def", 2)
import models brings in the module and leaves its names behind the module’s own. from models import Question brings that one name directly into the current file.

Both are ordinary and correct. They differ in what a reader sees at the point of use.
Choosing between them
models.Question(...) says where the class came from, every time it is used. That is valuable when a name is ambiguous on its own, and it is why nobody writes from datetime import *.
Question(...) reads better when the name is unmistakable and used often. Ten models.Question calls in a row is noise; the module name has stopped telling you anything you did not know by the second one.
The habit worth adopting:
| Situation | Prefer |
|---|---|
| A few well-named classes you use constantly | from models import Question, Quiz |
A name that means little alone (load, run, parse) | import loader, then loader.load(...) |
| Two modules exporting the same name | import both, and let the prefix separate them |
For this project, from models import Attempt, Question, Quiz is the better line. All three names are specific, and main.py uses them repeatedly.
report.py and models.py both define a function called describe. What should the file using both do?
Where Python looks
from models import Question sends Python looking for models.py, and the first place it looks is the directory of the program being run. That is why importing a file you wrote beside main.py simply works, with no configuration anywhere.
It is also why the file the program starts from matters, which is Lesson 5.
One consequence worth knowing now: a file of yours named random.py or json.py would be found before the standard library’s, and every import of that name in the whole program would get yours. The error that follows is genuinely confusing, and the fix is to not name a module after something in the standard library.
The exercise
main.py gets its import line, and it is worth appreciating what happens next: the program keeps its behavior, uses the shared definitions in models.py, and now says at the top exactly what it depends on.
Task
Repair main.py, which this lesson’s editor owns.
Import Attempt, Question, and Quiz from models at the top of the file, and delete the three class definitions that moved there in the previous lesson. Everything else stays: report_lines, build_quiz, main, and the guard at the bottom.
Use from models import Attempt, Question, Quiz. All three names are specific and used repeatedly here, which is the case that style suits.
Run the program. It should print exactly what it printed before the split, which is the point: the structure changed and the behavior did not.