Skip to content
aviral gupta

// B5.1 · ~25 min · Beginner

Modules and imports

After this lesson you can split a program into main.py and a helpers.py module, import it in the right form, and keep script-only code behind if __name__ == "__main__":.

Lesson 1 of 5 in B5 Modules, files and errors

Start of the module

You will be able to

  • Import a module or single names from it with import and from … import
  • Split a program into main.py and a second file, guarding script code with if __name__ == "__main__":
  • List what a module defines with dir() and explain where Python looks for a module
  1. Warm-up · Activity 1 of 7

    A module is mostly a file of functions. Warm-up from module B3: which statements about def are true? Pick all that apply.

    Select all that apply.

  2. Predict · Activity 2 of 7

    Predict before you read on. dir() with no argument lists the names defined in this file. What does this print?

    from math import sqrt
    
    print(sqrt(25), "math" in dir())
  3. Practice · Activity 3 of 7

    Fill in the keyword so that the name math refers to the whole math module.

    ____ math
    math
  4. Practice · Activity 4 of 7

    This file is saved as main.py and run with python main.py. What does it print?

    print("name is", __name__)
  5. Practice · Activity 5 of 7

    Match each import to the name or names it gives you.

  6. Brain teaser · Activity 6 of 7

    Brain teaser. What does this print?

    from math import pi
    
    pi = 3
    
    import math
    print(pi, math.pi > 3)
  7. Apply · Activity 7 of 7

    Mini-task, on your own computer. Create temperature.py with a function c_to_f(celsius) that returns celsius * 9 / 5 + 32, and a self-test under if __name__ == "__main__":. Then, in main.py in the same folder, import it and print a line such as 0 C = 32.0 F for 0, 20 and 100. Run both files.

    Check your work against this list

Build it yourself

Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.

Worked example

A main program and its helpers module

Two files sit side by side: helpers.py defines two text functions, and main.py imports them in both forms. helpers.py prints a line at the top level, so you can see when it runs, and keeps its self-test under the __main__ guard. Run main.py, then look at which of helpers.py’s lines appeared.

main.py

import helpers
from helpers import word_count

sentence = "modules keep programs tidy"

print(helpers.shout(sentence))
print("words:", word_count(sentence))
print("helpers.__name__ is", helpers.__name__)
print("__name__ here is", __name__)
print([name for name in dir(helpers) if not name.startswith("_")])

helpers.py

"""Small text helpers, imported by main.py."""


def shout(text: str) -> str:
    return text.upper() + "!"


def word_count(text: str) -> int:
    return len(text.split())


print("loading helpers")

if __name__ == "__main__":
    # Runs only with: python helpers.py
    print("self-test:", shout("hi"), word_count("a b c"))

Run it with

python main.py

Output

loading helpers
MODULES KEEP PROGRAMS TIDY!
words: 4
helpers.__name__ is helpers
__name__ here is __main__
['shout', 'word_count']
  • loading helpers appears once, although main.py imports helpers twice: top-level code runs on the first import only.
  • The self-test did not run, because helpers.py’s __name__ is "helpers" when it is imported.
  • main.py uses helpers.shout with the module name, but word_count on its own, because it was imported with from.
  • dir(helpers) also lists names such as __name__ and __doc__; the list comprehension drops everything starting with _.
Change it and run it

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.

Exercises

Exercise 1 of 2

Use the helpers module

helpers.py (the second tab) defines average(scores) and grade(score). Leave it as it is. In main.py, read one line of whole-number scores separated by spaces, then print average: and the average, and grade: and its grade. Use import helpers for average and from helpers import grade for grade. For 80 65 the output is average: 72.5 and grade: C.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.

Hints
  1. Hint 1

    Start main.py with two lines: import helpers, then from helpers import grade.

  2. Hint 2

    After import helpers, average is reached through the module: helpers.average(scores). grade is called on its own: grade(mean).

  3. Hint 3

    print("average:", mean) puts a space between the label and the number for you.

Show a solution

One way to solve it. Yours can look different and still pass the checks.

import helpers
from helpers import grade

scores = [int(word) for word in input().split()]
mean = helpers.average(scores)
print("average:", mean)
print("grade:", grade(mean))
Run it on your computer

Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.py

# helpers.py, next to this file, defines average(scores) and grade(score).
# Import them, then print the average and the grade of the scores typed in.
scores = [int(word) for word in input().split()]
print("average:", 0)

test_main.py

from learnrun import run_main


def report(line):
    return run_main(line + "\n").strip().splitlines()


def test_average_and_grade():
    """80 65 gives average 72.5 and grade C"""
    got = report("80 65")
    assert got == ["average: 72.5", "grade: C"], f"for 80 65 the program printed {got!r}"


def test_top_grade():
    """90 100 gives average 95.0 and grade A"""
    got = report("90 100")
    assert got == ["average: 95.0", "grade: A"], f"for 90 100 the program printed {got!r}"


def test_uses_helpers():
    """main.py imports helpers instead of copying its functions"""
    with open("main.py", encoding="utf-8") as f:
        source = f.read()
    assert "import helpers" in source, "main.py has no line import helpers"
    assert "from helpers import grade" in source, "main.py has no line from helpers import grade"
    assert "def " not in source, "main.py defines its own function; use the ones in helpers.py"

helpers.py

"""Score helpers for main.py. Leave this file as it is."""


def average(scores: list[int]) -> float:
    return sum(scores) / len(scores)


def grade(score: float) -> str:
    if score >= 90:
        return "A"
    if score >= 75:
        return "B"
    if score >= 50:
        return "C"
    return "F"

On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.

Run the program:

python main.py

Run the checks (needs learnrun.py in the same folder):

python learnrun.py test
Download learnrun.py

Exercise 2 of 2

Make main.py importable

main.py defines greet(name), which uses shout from helpers.py, and then asks for a name and prints the greeting. The tests import main.py to call greet, and at that moment nothing may be read or printed. Move the input and print lines under if __name__ == "__main__": so they run only when main.py itself is run.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.

Hints
  1. Hint 1

    When a test imports main.py, its __name__ is "main". When you run it, its __name__ is "__main__".

  2. Hint 2

    Add the line if __name__ == "__main__": at the bottom and indent the two lines below it by four spaces.

  3. Hint 3

    Leave the def where it is: the tests need greet to exist after the import.

Show a solution

One way to solve it. Yours can look different and still pass the checks.

from helpers import shout


def greet(name: str) -> str:
    return shout("hello, " + name)


if __name__ == "__main__":
    name = input()
    print(greet(name))
Run it on your computer

Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.py

from helpers import shout


def greet(name: str) -> str:
    return shout("hello, " + name)


name = input()
print(greet(name))

test_main.py

import contextlib
import importlib
import io
import sys

from learnrun import run_main


def import_main():
    sys.modules.pop("main", None)
    printed = io.StringIO()
    saved = sys.stdin
    sys.stdin = io.StringIO("")
    try:
        with contextlib.redirect_stdout(printed):
            module = importlib.import_module("main")
    finally:
        sys.stdin = saved
    return module, printed.getvalue()


def test_import_is_quiet():
    """Importing main.py reads no input and prints nothing"""
    try:
        _, printed = import_main()
    except EOFError:
        assert False, "importing main.py called input(): move it under if __name__ == \"__main__\":"
    assert printed == "", f"importing main.py printed {printed!r}: move the print under if __name__ == \"__main__\":"


def test_greet():
    """greet(\"ada\") returns HELLO, ADA!"""
    try:
        module, _ = import_main()
    except EOFError:
        assert False, "main.py could not be imported: it called input() at the top level"
    got = module.greet("ada")
    assert got == "HELLO, ADA!", f"greet(\"ada\") returned {got!r}, expected 'HELLO, ADA!'"


def test_run_as_script():
    """Run as a script, it still asks for a name and greets it"""
    got = run_main("Grace\n").strip()
    assert got == "HELLO, GRACE!", f"for Grace the program printed {got!r}, expected 'HELLO, GRACE!'"

helpers.py

"""Text helpers for main.py. Leave this file as it is."""


def shout(text: str) -> str:
    return text.upper() + "!"

On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.

Run the program:

python main.py

Run the checks (needs learnrun.py in the same folder):

python learnrun.py test
Download learnrun.py

Common mistakes

Writing the file name in the import

import math.py

print(math.sqrt(16))

What Python prints

ModuleNotFoundError: No module named 'math.py'; 'math' is not a package

Why, and the fix

import takes a module name, not a file name. Python reads math.py as "the module py inside the package math", which does not exist. Write import math, or import helpers for your own helpers.py.

Using the module name after from … import

from math import sqrt

print(math.sqrt(16))

What Python prints

NameError: name 'math' is not defined. Did you forget to import 'math'?

Why, and the fix

from math import sqrt defines only sqrt, not math. Either call sqrt(16) directly, or change the import to import math and keep math.sqrt(16). Pick one form per module and use it consistently.

Calling a function without its module name

import math

print(sqrt(16))

What Python prints

NameError: name 'sqrt' is not defined

Why, and the fix

import math binds only the name math. Its functions stay inside it: write math.sqrt(16). If you want the short name, import it: from math import sqrt.

Python in the browser: Pyodide 314.0.7, MPL-2.0. Licence and source

Exit ticket

5 questions, no hints. Score 80% or more to complete the lesson.

Finish every activity above to unlock the exit ticket.

Report a problem

Spotted something wrong or unclear? Say what, and it will be checked and fixed.

#

At least 20 characters.

Only if you want a reply.

Key ideas

Two ways to import

A module is a file of Python definitions: helpers.py is the module helpers. import helpers gives you one new name, helpers, and you reach its contents with a dot: helpers.shout("hi"). from helpers import shout copies just the name shout into your file, so you call shout("hi"), but the name helpers itself is not defined. import math as m binds the module under a shorter name. Never write the .py in an import: the statement takes a module name, not a file name.

Top-level code runs once, and __name__ tells you why

Importing a module runs its top-level code, only the first time it is imported. Inside every module, the variable __name__ holds its name: "helpers" when it is imported, but "__main__" when the file is the one you ran with python helpers.py. So code under if __name__ == "__main__": runs when you run the file, and not when another file imports it. Put demos, input() and print() calls there, and the file works both as a script and as a module.

dir() and the module search path

dir(helpers) returns a sorted list of the names helpers defines, including dunder names such as __name__; filter out names starting with _ to see its functions. dir() with no argument lists the names in your own file. To find a module, Python checks its built-in modules, then the folders in the list sys.path. The first folder is the one holding the script you ran, which is why helpers.py must sit next to main.py. If nothing matches, you get ModuleNotFoundError.

Sources

Last reviewed September 29, 2026