Organizing a Program Across Files · practice
Import Styles and Import-time Code
An import does more than make a name available. It runs the file, top to bottom, once.
That sentence explains most of the surprising behavior beginners meet with imports, so it is worth watching happen.
What import actually does
Suppose noisy.py contains this:
print("noisy.py is being imported")
GREETING = "hello"
def shout():
return GREETING.upper()
Importing it prints. Not when shout is called: at the moment of the import.
import noisy # prints "noisy.py is being imported"
print(noisy.shout()) # prints "HELLO"
Every top-level line runs. print prints, GREETING = "hello" assigns, and the def statement creates a
Once in each run
Within one program run, importing the same
import noisy # prints
import noisy # prints nothing
Python keeps a record of what it has already imported and hands back the same module import models without three copies of Question existing, and it is what makes main.Question is models.Question true.
During one program run, both main.py and report.py import models normally. How many times does models.py run?
Why this matters for how you write a module
A module that does work when imported forces that work on everyone who imports it, whether they wanted it or not.
# scoring.py, written carelessly
def percentage(score, total):
...
# a quick demo the author left in
print(percentage(4, 10))
Import scoring from anywhere and it prints. Your test suite prints. A web request prints. There is no way to use percentage quietly, because the demo is part of the file rather than part of a program.
The fix is the guard you have been looking at since Chapter 12 of Fundamentals I:
def percentage(score, total):
...
if __name__ == "__main__":
print(percentage(4, 10))
__name__ is a "__main__". When the file is imported it is the module’s own name, "scoring". So the guarded block runs when you run the file and stays quiet when someone imports it.
That is the whole mechanism, and it turns a file into two things at once: a module others can use, and a program you can run.
The rule this gives you
A module’s top level should define things, not do things.
Definitions, constants, and imports at the top level are fine, because they are what the module is. Printing, asking for input, opening files, and calling functions belong inside a function, or behind the guard.
from models import * is worth naming here too, since it is the other import style beginners meet. It pulls every public name from a module into the current file. That means you cannot tell where a name came from by reading, two modules can silently overwrite each other’s names, and adding a name to a module can break a file that never mentioned it. Use it in a throwaway REPL session if you like. Do not use it in a file.
The exercise
You are writing scoring.py, the module that turns a score into a percentage and a grade. It has a demo at the bottom that currently runs on import.
Task
Create scoring.py, which this lesson’s editor owns.
It should define two
percentage(score, total)returns the score as a fraction between0.0and1.0, and returns0.0whentotalis zero rather than raising;grade_letter(fraction)returns"A"at0.9or above,"B"at0.8,"C"at0.7,"D"at0.6, and"F"below that.
Keep the demo at the bottom, but put it behind if __name__ == "__main__": so it runs when you run the file and stays silent when another
The grader checks both quiet importing and the direct-file demo. The project Run button still executes main.py; selecting scoring.py does not change that. Submit checks the scoring file, and the next lesson connects its functions to the running application.