Skip to main content
CodeFlow
control flow~10 minRoute 01 12 of 12

Modules & Imports

Splitting a program across files — and naming what comes in.

Prerequisites:Functions

Story — chapters in a book

A 5,000-line program in one file is a wall of text — nothing tells you which lines belong together, what the "public face" is, what's for internal use only. Modules are how you split a program into chapters. Each module is one file (or one folder) with a name, a clear purpose, and a list of names it shares with the rest of the program.

Imports are how one module pulls in names from another. Two flavours: namespace import (import math — call as math.sqrt) versus named import (from math import sqrt — call as sqrt). Trade source-visibility for brevity.

Modula says

Two rules of thumb: (1) one module, one responsibility — if you can't summarize a file's purpose in a sentence, split it. (2) Prefer namespace imports for clarity; use named imports for things you call dozens of times.

See it — the public face of a file

Every language has a way to say "this name is part of my public interface": Python's convention (no underscore prefix), JavaScript's export, Java's public, Go's capitalized identifier. Anything not exported is private to the module — callers can't reach it.

Try it — would you import or duplicate?

Exercise. You have a 200-line file auth.py with login, signup, password-reset, and a dozen small helpers. A new file profile.py needs the password-reset helper but nothing else. What's the cleanest move?

Reveal answer

from auth import reset_password. Importing one symbol doesn't pull in the whole module's code at runtime cost (Python imports the module once and caches it), but it does signal in the reader's head "this file uses one specific thing from auth." Duplicating the helper would create two places to fix the same bug.

Code it — five languages, one idea

# math_utils.py
def square(x):
    return x * x

# main.py
import math_utils
print(math_utils.square(5))      # 25

from math_utils import square
print(square(5))                  # 25 — name brought into scope

Modula says

import what you actually use; namespace what could collide. The two-line difference between math.sqrt(x) and from math import sqrt is everything — one keeps the source visible, the other hides it.

Quiz it — make it stick

  1. Question 1

    In Python, what is the difference between `import math` and `from math import sqrt`?

  2. True or false — Question 2

    A circular import (A imports B, B imports A) always crashes the program at startup.

  3. Predict — Question 3

    What's wrong with `from foo import *` in production code?

    # in module foo:
    def helper(): ...
    status = "ok"
    
    # in your code:
    from foo import *
    status = "draft"   # quietly shadowed?

No Dumb Questions

Real questions other learners asked on this page.

  • What's a module versus a package?
    A module is one file. A package is a folder of modules grouped together (often with an __init__ file or index file marking it). Same idea, different scope.
  • Where does the language find an imported file?
    It searches a list of paths — Python's sys.path, Node's node_modules walk, Java's classpath. Packaging tools (pip, npm, Maven) write to those paths so installed code is findable.
  • Why are circular imports a problem?
    A imports B, B imports A — neither can finish loading because each is waiting for the other. Languages handle it differently (some lazy-load, some throw), but the fix is always to break the cycle by splitting code.