0%

Organizing a Program Across Files · practice

Modules and Namespaces

For the you write in this chapter, a module is a Python file containing related definitions. Python also provides modules in other forms, but an ordinary .py file is all this project needs.

What makes a module useful is that its names belong to it. Two files can both define Question without colliding, and code that uses one has to say which.

Importing a file you wrote

Chapter 8 borrowed a tool from the standard library:

from dataclasses import dataclass

The same statement reaches a file of your own. If your project contains models.py, then models is a module you can import:

import models

question = models.Question("Which keyword starts a function?", "def", 2)

With this project started from main.py, Python finds models.py in the project directory, runs it once, and hands you an holding everything that file defined. models.Question reads out of that object.

Note what is not there: no path, no .py, and no quotes. You import the module’s name, and the name is the filename without its extension.

Namespaces, concretely

Try it

Now imagine a second file, survey.py, that also defines Question for its own purposes. In one file those two would fight over the name. As modules they do not:

import models
import survey

quiz_question = models.Question("Which keyword starts a function?", "def", 2)
survey_question = survey.Question("How difficult was this?")

models.Question and survey.Question are different classes, and the code says which one it means every time. That is what a namespace is: a place names live, so the same name in two places stays two things.

This is the same idea as self.answer naming an attribute on one object rather than a loose , one level up.

Your project has a file models.py. Which line imports the module under the name models, so you can write models.Question(...)?

Splitting is moving, not rewriting

The move you are about to make is the plainest kind of refactoring: text leaves one file and arrives in another, unchanged.

It is worth being deliberate anyway, because the interesting question is not how to move a class, it is which classes belong together. Question, Quiz, and Attempt belong in the same module because they are the same kind of thing: the vocabulary the program is about, and the rules that go with it. That grouping has a name, the domain, and Lesson 6 gives it a companion.

What stays behind is everything that is not vocabulary: turning an attempt into lines of text, assembling a particular quiz, and running the program. Those are jobs done with the domain rather than parts of it.

The exercise

Copy Question, Quiz, and Attempt from main.py into the supplied models.py file, exactly as they are.

This lesson opens models.py. Select main.py in the file panel to read the original classes, then return to models.py to copy them.

Leave main.py unchanged for this step, so Run still executes the working original. The next lesson finishes the move by deleting its duplicate classes and importing the new module. Submit here checks only models.py.

Task

Copy Question, Quiz, and Attempt into the supplied models.py.

Copy all three classes across exactly as they are in main.py. This is a move, not a rewrite: no behavior should change, and nothing needs renaming.

Those three are the program’s vocabulary, so they belong together. models.py should contain them and nothing else: leave report_lines, build_quiz, and main where they are.

Leave the original classes in main.py for now. Run still uses that original program; Submit checks models.py. The next lesson deletes the duplicates and imports them instead, completing the move.