Modules, Imports, and Entrypoints
What You'll Learn
How Python's module system works, how to import code, and how to structure a script so it runs correctly both directly and as an imported module.
What Is a Module?
A module is any .py file. When you import it, Python runs the file and makes its names available:
myapp/
├── main.py
├── utils.py
└── config.py
utils.py:
def format_name(name: str) -> str:
return name.strip().title()
TIMEOUT = 30
main.py:
import utils
name = utils.format_name(" alice ")
print(name) # Alice
print(utils.TIMEOUT) # 30
Import Styles
# Import the whole module — access with module.name
import os
import json
import pathlib
os.getcwd()
json.dumps({})
# Import specific names — use directly
from os.path import join, exists
from datetime import datetime, timedelta
join("/home", "alice") # no 'os.path.' prefix needed
# Import with alias — for long names
import numpy as np
import pandas as pd
from datetime import datetime as dt
np.array([1, 2, 3])
# Import all (avoid in production code — pollutes namespace)
from math import * # ❌ unclear where names come from
The Standard Library
Python ships with a huge standard library — no pip install needed:
# File system
import os
import pathlib
import shutil
import tempfile
# Data formats
import json
import csv
import configparser
# Dates and time
from datetime import datetime, date, timedelta
import time
# Text processing
import re
import string
# Math
import math
import random
import statistics
# System
import sys
import subprocess
import signal
# Networking
import urllib.request
import http.server
# Utilities
import itertools
import functools
import collections
import copy
import hashlib
Packages
A package is a directory containing an __init__.py file:
myapp/
├── __init__.py ← makes this a package
├── main.py
├── utils/
│ ├── __init__.py
│ ├── text.py
│ └── files.py
└── config.py
Importing from a package:
from myapp.utils.text import format_name
from myapp.utils import files
import myapp.config as cfg
The if __name__ == "__main__": Guard
This is crucial for any script that might also be imported:
# utils.py
def format_name(name: str) -> str:
return name.strip().title()
# This runs when you execute utils.py directly:
# python3 utils.py
# But NOT when another file imports it:
# import utils
if __name__ == "__main__":
test_name = " alice smith "
print(format_name(test_name)) # Alice Smith
Without this guard, running import utils in another file would also print to screen — unexpected side effect.
The main() Pattern
The standard way to structure a runnable script:
#!/usr/bin/env python3
"""Process user records and generate a report."""
import sys
import json
from pathlib import Path
def load_records(path: Path) -> list[dict]:
"""Load JSON records from a file."""
with open(path, encoding="utf-8") as f:
return json.load(f)
def filter_active(records: list[dict]) -> list[dict]:
"""Return only active records."""
return [r for r in records if r.get("active", False)]
def report(records: list[dict]) -> None:
"""Print a summary of records."""
print(f"Total records: {len(records)}")
for r in records:
print(f" - {r['name']}")
def main() -> int:
if len(sys.argv) < 2:
print("Usage: script.py <records.json>", file=sys.stderr)
return 1
path = Path(sys.argv[1])
records = load_records(path)
active = filter_active(records)
report(active)
return 0
if __name__ == "__main__":
sys.exit(main())
Benefits:
main()can be tested by calling it from tests- Clean entry point
- Proper exit codes
Relative vs Absolute Imports
Inside a package, use relative imports to avoid hardcoding the package name:
# Absolute import (works anywhere)
from myapp.utils.text import format_name
# Relative import (only inside a package)
from .text import format_name # same package
from ..config import TIMEOUT # parent package
For scripts (not packages), always use absolute imports.
sys.path — Where Python Looks for Modules
import sys
print(sys.path) # list of directories Python searches for modules
Python searches in order:
- Current directory
PYTHONPATHenvironment variable- Standard library
- Site packages (pip installs)
Add a path at runtime:
sys.path.insert(0, "/path/to/my/modules") # use sparingly
Common Mistakes
| Mistake | Fix |
|---|---|
from module import * | Import specific names only |
| Code at module level with side effects | Guard with if __name__ == "__main__": |
| Circular imports | Restructure — put shared code in a third module |
| Shadowing stdlib names | Don't create json.py, os.py, etc. |
Quick Reference
# Import
import module
from module import name
from module import name as alias
import module as alias
# Entrypoint
def main() -> int:
...
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
# Standard library highlights
import os, sys, json, re, math, random
import pathlib, shutil, subprocess
from datetime import datetime, timedelta
from collections import defaultdict, Counter
from itertools import chain, islice
from functools import lru_cache, partial