25. Modules and import

Every script you have written so far has lived in one file. Real programs grow past that. Python splits programs across files using modules: a module is a .py file that exposes functions (and variables) for other files to use.

A module is just a Python file

No special wrapper is needed. Create a file, put functions in it, and it is a module:

# file: greet.py

def hello(name):
    print(f"Hello, {name}!")

def bye(name):
    print(f"Goodbye, {name}.")

Two things to notice:

  • The file does no work when imported. Nothing prints, nothing runs. It only defines functions.
  • There is no return statement, no wrapper table, no ceremony. A .py file is a module by virtue of existing.

Using a module with import

Another file loads it with import:

# file: main.py
import greet

greet.hello("Keiko")
greet.bye("Keiko")

import greet does three things:

  1. Finds a file called greet.py on the Python path.
  2. Runs it (only the first time — see the note below).
  3. Binds the name greet in the current file so you can access its contents via greet.hello, greet.bye, etc.

Python caches imported modules. If two files both import greet, Python loads greet.py once and both files share the same module object. This is good — it means modules are cheap to import from anywhere.

from module import name

If you only need one or two names from a module, import them directly:

from greet import hello

hello("Keiko")   # no "greet." prefix needed

To import several names at once:

from greet import hello, bye

This puts hello and bye directly into the current namespace. There is no greet. prefix required, but there is also no greet. to tell a reader where these functions came from. For small, clearly named functions it is fine; for large modules, import greet and using greet.hello is clearer.

import module as alias

Long module names can be given a shorter alias:

import math_helpers as mh

print(mh.double(7))
print(mh.triple(7))

This is common in the Python ecosystem — you will see import numpy as np and import pandas as pd everywhere. For your own small modules, aliases are optional.

Where Python looks

Python looks in the current working directory first, then a list of standard library and installed-package paths. The simplest layout is to put main.py and greet.py in the same folder, then run from that folder:

cd exercises/25/examples
python main.py

If you stay at the repo root and run python exercises/25/examples/main.py, Python may not find greet.py because the search starts from the script's directory. Run from the same folder to keep it simple.

Private helpers inside a module

Functions that you do not want other files to call can be left as plain functions — just do not mention them in the module's documentation or expose them via from module import *. The convention is to prefix their names with an underscore:

# file: math_helpers.py

def _clamp(x, lo, hi):
    if x < lo:
        return lo
    if x > hi:
        return hi
    return x

def double(x):
    return _clamp(x * 2, -100, 100)

_clamp starts with _, signalling "internal use only". Other files can call math_helpers._clamp if they try, but the underscore is a strong convention that says not to.

Homework

The Part 5 homework uses two files per problem: a module file and a main.py that uses it. Each problem lives in its own sub-folder so the two files sit together. Open a terminal, cd into that folder, then run python main.py.

Problem 1 — Greet module

Folder: exercises/25/homework/01-greet-module/.

Finish greet.py so it exposes hello(name) and bye(name). The main.py file is provided and already calls both functions.

Problem 2 — Math helpers

Folder: exercises/25/homework/02-math-helpers/.

Build a math_helpers.py module with at least two functions:

  • double(x) returns x * 2.
  • triple(x) returns x * 3.

main.py calls both and prints the results.

Problem 3 — Counter

Folder: exercises/25/homework/03-counter/.

Build a counter.py module that exposes three functions:

  • increment() — increases the counter by 1.
  • get() — returns the current value.
  • reset() — sets the counter back to 0.

The counter itself is a module-level variable inside counter.py. It must persist across calls (think back to the scope discussion at the end of Chapter 21).

Challenge — String utilities

Folder: exercises/25/homework/04-string-utils/.

Build a string_utils.py module with at least three functions of your own. Suggestions:

  • shout(s) returns s in upper case with ! appended.
  • echo(s, n) returns s repeated n times with spaces between.
  • reverse_words(s) returns the words of s in reverse order.

The last one is a stretch — you will need to split the string into words and reverse the list. If that is too much, swap it for anything else of your own.

Stuck or finished? Open the homework solutions page.