0%

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 . Defining a function is itself something that happens at import time; calling it is not.

Once in each run

Within one program run, importing the same twice normally runs its code once:

import noisy          # prints
import noisy          # prints nothing

Python keeps a record of what it has already imported and hands back the same module . This is why three files can all 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 Python sets in every module. When a file is run directly it is "__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 and nothing that runs on import:

  • percentage(score, total) returns the score as a fraction between 0.0 and 1.0, and returns 0.0 when total is zero rather than raising;

  • grade_letter(fraction) returns "A" at 0.9 or above, "B" at 0.8, "C" at 0.7, "D" at 0.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 imports it.

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.