Modules and Imports

Updated

September 8, 2026

Modules and Imports

A module is a .py file. import runs that file once and binds a name to the module object. The boring default is import prices (or from prices import cents when one name is the whole API). Keep a folder of files, not a pile of copy-paste.

Mental model

import prices looks for prices.py (or a package named prices) on sys.path. The first time, Python executes the file, stores the module in sys.modules, and binds the name prices in your module. The second import reuses sys.modules. It does not run the file again.

from prices import cents still loads the whole module. It then binds cents in your namespace. The name prices is not bound unless you also imported it.

import prices as p binds a shorter name. Use as when two modules share a last component, or when the real name is awkward.

importlib.import_module("prices") does the same lookup from a string. That is how you load a plugin name from config. You still need a real module on the path.

Worked examples

Put every file in this chapter in the same folder. Run the commands from that folder.

Case 1: import a sibling file

Save as prices.py:

# prices.py
MENU = {"latte": 400, "tea": 250, "bun": 150}


def cents(name):
    if name not in MENU:
        raise KeyError(f"unknown item {name!r}")
    return MENU[name]

Save as order.py. This is the program you run.

# order.py
import prices


def total(names):
    return sum(prices.cents(n) for n in names)


def main():
    print(prices.cents("latte"))
    print(total(["latte", "tea"]))


if __name__ == "__main__":
    main()

Run:

uv run python order.py

Output:

400
650

prices.py has no main guard. It is a library file. order.py is the program.

Case 2: from import

Save as from_order.py. Only cents is bound here. MENU is still on the module, but this file cannot see the name prices.

# from_order.py
from prices import cents


def main():
    print(cents("bun"))
    print(cents.__module__)


if __name__ == "__main__":
    main()

Run:

uv run python from_order.py

Output:

150
prices

cents.__module__ still says prices. You imported the function, not a copy of its code.

Case 3: import ... as

Save as alias_order.py. The alias is local to this file.

# alias_order.py
import prices as menu


def main():
    print(menu.cents("tea"))
    print(menu.MENU["tea"])


if __name__ == "__main__":
    main()

Run:

uv run python alias_order.py

Output:

250
250

Case 4: importlib from a string

Save as load_prices.py. The module name comes from a variable. That is the only reason to use importlib on day one.

# load_prices.py
import importlib


def main():
    name = "prices"
    mod = importlib.import_module(name)
    print(mod.cents("latte"))
    print(mod.__name__)


if __name__ == "__main__":
    main()

Run:

uv run python load_prices.py

Output:

400
prices

If name is wrong, you get ModuleNotFoundError. Do not catch that and keep going unless you are probing optional plugins on purpose.

The trap

from prices import cents does not bind prices. Reaching for prices.MENU looks natural and is a NameError.

Save as missing_module_name.py:

# missing_module_name.py
from prices import cents


def main():
    print(cents("latte"))
    try:
        print(prices.MENU)
    except NameError as e:
        print(type(e).__name__ + ":", e)


if __name__ == "__main__":
    main()

Run:

uv run python missing_module_name.py

Output:

400
NameError: name 'prices' is not defined

The fix is import prices (then prices.cents and prices.MENU), or from prices import cents, MENU. Do not add from prices import *. Star imports dump unknown names into your file and make collisions invisible.

The boring rule

  • One idea per module. A module name is a file name without .py.
  • Prefer import prices when you will use several names. Prefer from prices import cents when one function is the API.
  • Use as for a clash or a long name, not for decoration.
  • importlib.import_module is for a name you did not know when you wrote the file.
  • Never from module import * in application code.
  • Library modules stay quiet at import time. Work happens in functions. The main guard lives in the program file.

Try this

  1. Add cookie to MENU at 200 cents. Run order.py again and include "cookie" in total.
  2. In from_order.py, also import MENU and print sorted(MENU).
  3. In load_prices.py, set name = "no_such_desk" and catch ModuleNotFoundError. Print the exception type.
  4. Split total into its own file totals.py that import prices. Have order.py import totals and print totals.total(["tea", "tea"]).