Beginner · Lesson 31 of 94

Modules, packages, and imports

Watch this lesson

View full playlist ↗

Read the lesson

What you'll learn

Concept

Every .py file is a module. When you import helpers, Python finds helpers.py, runs it top to bottom once, and gives you its names under helpers., so helpers.clean(text) calls the function defined there. from helpers import clean brings the one name in directly. Running a file once and caching the result is why imports are cheap to repeat and why code at the top level of a module runs at import time, whether you wanted it to or not.

Modules, packages, and imports: main.py; import helpers → find helpers.py on the search path → run it once, top to bottom → cache it in sys.modules → helpers.clean is available
Modules, packages, and imports · Figure 1 · Open for full size

Where does Python look? The folder of the script you ran comes first, then the standard library, then installed packages. That first entry is why import helpers works when helpers.py sits next to your script and fails when it does not. The full list is sys.path; you rarely change it, but knowing it exists explains most "module not found" errors.

Every module has a __name__. When a file is run directly, its __name__ is "__main__"; when it is imported, its __name__ is its module name. The main guard, if __name__ == "__main__":, therefore means "only when this file is the program, not when it is imported". Put the code that starts your program under it, and the same file becomes both a runnable script and an importable library of functions.

Situation __name__ in tools.py Code under the main guard
python tools.py "__main__" runs
import tools from elsewhere "tools" does not run
from shop.tools import x "shop.tools" does not run

A package is a folder of modules with an __init__.py file, which can be empty. It gives related modules one namespace: from shop.pricing import tax. Inside a package, modules import their siblings with relative imports, from .pricing import tax, so the package can be renamed or moved without editing every file. __all__, a list of names in a module, declares its public API: from module import * imports only those, and readers know what is meant for outside use.

Modules, packages, and imports: shop/ (package) → __init__.py → pricing.py: tax() → cart.py: from .pricing import tax → main.py: from shop.cart import total
Modules, packages, and imports · Figure 2 · Open for full size

A circular import happens when a imports b and b imports a. Python starts running a, hits the import of b, runs b, which imports the half-finished a and fails to find names that have not been defined yet. The fix is structural: move the shared code into a third module both can import, or import inside the function that needs it. The traceback is the clue: it shows circular_a importing circular_b, which imports circular_a again, and then a name that does not exist yet. Depending on the situation Python's last line says "partially initialized module" or suggests renaming the file; either way, the loop in the traceback is the real cause.

Code

Two files: a module of functions and a script that imports them. The module's own test code is under a main guard, so importing it prints nothing.

# textkit.py
"""Small text helpers."""

__all__ = ["clean", "initials"]


def clean(text):
    return " ".join(text.split()).strip().lower()


def initials(name):
    return "".join(part[0] for part in clean(name).split()).upper()


def _debug(text):
    return repr(text)


if __name__ == "__main__":
    print("running textkit directly:", clean("  Hello   World "))
Output
running textkit directly: hello world
# main.py
import textkit
from textkit import initials

print(textkit.__name__, "|", __name__)
print(textkit.clean("  Ada   Lovelace "))
print(initials("grace brewster hopper"))
print(textkit.__all__)
Output
textkit | __main__
ada lovelace
GBH
['clean', 'initials']

A package with a relative import inside it. cart.py reaches its sibling with a leading dot, and main imports through the package name.

# shop/__init__.py
"""The shop package."""
# shop/pricing.py
TAX_RATE = 0.2


def tax(amount):
    return round(amount * TAX_RATE, 2)
# shop/cart.py
from .pricing import tax


def total(prices):
    subtotal = sum(prices)
    return subtotal + tax(subtotal)
# use_shop.py
from shop.cart import total
from shop import pricing

print(pricing.__name__)
print(total([10.0, 25.0]))
Output
shop.pricing
42.0

A circular import caught in the act. Read the traceback top to bottom: run_circular imports circular_a, which imports circular_b, which imports circular_a and asks for greet before that line has run. The last block shows that the first entry on the search path is the script's own folder.

# circular_a.py
import circular_b

def greet():
    return "a greets " + circular_b.name()
# circular_b.py
import circular_a

def name():
    return "b"

print(circular_a.greet())
# run_circular.py
import circular_a
Output
Traceback (most recent call last):
  File "run_circular.py", line 1, in <module>
    import circular_a
  File "circular_a.py", line 1, in <module>
    import circular_b
  File "circular_b.py", line 6, in <module>
    print(circular_a.greet())
          ^^^^^^^^^^^^^^^^
AttributeError: module 'circular_a' has no attribute 'greet' (consider renaming 'circular_a.py' if it has the same name as a library you intended to import)
# show_path.py
import sys
from pathlib import Path

print(Path(sys.path[0]).resolve() == Path(__file__).resolve().parent)
print(len(sys.path) > 2)
print(any("lib" in p for p in sys.path))
Output
True
True
True

Try it yourself

Create a package geometry/ with an empty __init__.py, a shapes.py defining area_circle and area_square, and a report.py that imports them with a relative import and defines describe(shape, size). Write main.py outside the package that imports describe and prints two descriptions. Give shapes.py a main guard that prints a quick self-test, and confirm it only prints when run directly.

Common mistakes

What's next

Your code can span many files. Next you will isolate each project's dependencies with virtual environments, pip, and uv.

Course overview