Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Python  Module 6 – Modules, packages and the standard library

Modules, imports and __name__

Split Python code into modules, import names in each form, guard script code with if __name__ == '__main__', and see how sys.path finds a module.

  • Beginner
  • 20 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free lesson, Pro answers: 20 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Predict how often a module's code runs and which names each form of import binds
  • Trace the folders of sys.path that Python searches for a module
  • Fix a file of your own that hides a standard module
  • Resolve a circular import by moving the shared name into its own module
  • Write a script whose demo code runs only under if __name__ == '__main__'

Before you start

What you’ll learn · Before you start

What you will learn

  • Predict how often a module's code runs and which names each form of import binds
  • Trace the folders of sys.path that Python searches for a module
  • Fix a file of your own that hides a standard module
  • Resolve a circular import by moving the shared name into its own module
  • Write a script whose demo code runs only under if __name__ == '__main__'

Before you start

On this page

One file stops being enough as a program grows: the word-counting functions of one script are soon wanted by a second script and by a test. A module: A file of Python code, such as textstats.py, that other files use with import. The first import in a program runs the file once and keeps the module object it built, so every later import gets that same object. is a file of Python code, such as textstats.py, and import textstats lets another file use what it defines; the program below imports it with three different statements, and the file still runs only once.

The first import finds the file, runs it from top to bottom and keeps the module object it built, so every later import gets that same object. Inside every module, the name __name__ says how the file is being used: '__main__' in the file you started as the program, and the module’s own name when another file imports it, which is how one file can be a reusable module and a runnable script at once. You will also trace where Python looks for a module and fix two mistakes that break imports: a file that hides a standard module, and two modules that import each other.

The first import runs the file, later imports reuse it

Here are two files from one folder. textstats.py defines three functions for counting words and has two lines of code at the top level that print; main.py imports it with three different statements. Each tab runs its own file as the program, with the other file beside it:

Two files, one program

Python (main.py) · first/main.py

import sys

import textstats
import textstats as ts
from textstats import word_count

line = "Practice makes progress, and progress makes practice easier."
print(word_count(line), "words")
print("Most common:", textstats.most_common(line))
print("One module object?", ts is textstats is sys.modules["textstats"])
print("Its name when imported:", repr(textstats.__name__))

Output

textstats.py is running, and its __name__ is 'textstats'
8 words
Most common: practice
One module object? True
Its name when imported: 'textstats'

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 main.py

Python (textstats.py) · first/textstats.py

"""Small helpers for counting the words of a text."""

PUNCTUATION = ".,;:!?\"'()"


def words(text):
    """The words of text in lower case, without punctuation at their ends."""
    cleaned = (w.strip(PUNCTUATION).casefold() for w in text.split())
    return [w for w in cleaned if w]


def word_count(text):
    return len(words(text))


def most_common(text):
    """The word used most often (on a tie, the one that appears first)."""
    counts = {}
    for w in words(text):
        counts[w] = counts.get(w, 0) + 1
    return max(counts, key=counts.get)


print("textstats.py is running, and its __name__ is", repr(__name__))

if __name__ == "__main__":
    # Runs only when this file is the program, never when it is imported.
    sample = "The cat sat on the mat. The end."
    print("Self-check:", word_count(sample), "words; most common:", most_common(sample))

Output

textstats.py is running, and its __name__ is '__main__'
Self-check: 8 words; most common: the

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 textstats.py

Three import statements, yet the line that textstats.py prints appears once. Step through what each statement did: an arrow to sys.modules is the import asking the module cache: The dictionary sys.modules, which maps the name of every module a program has imported to its module object. An import looks there first, which is why a module's file runs only once per program., and the sub-labels say what each part holds.

Interactive · message by message Three imports of textstats, one run of its file Step through lines 3 to 5 of main.py. An arrow to sys.modules is an import asking the module cache; the sub-labels say what each part holds.
Three imports of textstats, one run of its file, step 8 of 8. Messages so far: main.py to sys.modules: textstats?; sys.modules to textstats.py: run the file; textstats.py to main.py: the module object; main.py to sys.modules: textstats?; sys.modules to main.py: the same module; sys.modules to main.py: word_count.main.pytextstats, ts, word_countsys.modulestextstats: foundtextstats.pyran once, top to bottomtextstats?run the filethe module objecttextstats?the same moduleword_count

The drawing shows the whole scene; its 8 steps are listed below.

Every step as text
  1. The first import. Line 3, import textstats, is the first import of textstats in this program. Python asks sys.modules first, and textstats is not in it.
  2. Found and created. Python searches the folders of sys.path and finds textstats.py beside main.py. It creates an empty module object and puts it into sys.modules before it runs a single line of the file.
  3. The file runs once. Now the file runs from top to bottom. The three def statements create functions, and the print() at the top level runs: the first line of the output. __name__ is 'textstats', so the guarded self-check is skipped.
  4. The name is bound. The module is complete, and the import binds the name textstats in main.py to it. That is the whole of the first import statement.
  5. The second import. Line 4, import textstats as ts, asks sys.modules again. This time textstats is there, so Python neither searches the folders nor runs the file.
  6. Another name, same object. The import binds a second name, ts, to the module it found. Nothing is printed, and ts is textstats is True.
  7. One name from the module. Line 5, from textstats import word_count, also finds the module in sys.modules. It binds one name in main.py, word_count, to the function the module holds.
  8. What the run printed. The rest of main.py uses the three names. Its output has the line from textstats.py once, and One module object? True: three import statements, one module object, one run of its file.

The first import ran the file and stored the new module in the module cache, sys.modules, the dictionary of every module the program has imported so far; the other two found it there and only gave it another name. That is why ts is textstats is sys.modules["textstats"] is True: three names for one module object. Press Edit, add a fourth import textstats at the end of main.py and run it: the line from textstats.py still appears once.

Now look at the run of textstats.py itself. Its __name__ is '__main__' this time, and the self-check under if __name__ == "__main__": runs. When main.py imported the same file, __name__ was 'textstats', so that block was skipped while the print() above it still ran. Everything at a module’s top level runs on import: def statements only create functions, which is harmless, but a call, a print or a file being read happens for every program that imports the module. Keep that kind of code inside functions or under the guard.

Key idea

A module’s file runs once per program, on its first import; every later import, in any form and from any file, gets the module object that sys.modules keeps.

Checkpoint

Check: One run per program, then the cache

  1. Question 1 of 2 main.py runs import config, and later calls a function twice that also runs import config. config.py has one print() at its top level. How many times does that line print?

    Choose one answer.

    A hint

    Ask what an import does when the module is already in sys.modules.

    Show the answer: Question 1 of 2

    Answer: Once

    A module's file runs once per program, on its first import, and the module object stays in sys.modules. Every later import of the same name, in any file or function, finds it there, so the print() at the top of config.py runs once.

  2. Question 2 of 2 textstats.py prints its own __name__. What does it print when main.py imports it?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: 'textstats'

    Python sets __name__ to '__main__' in the file you start as the program and to the module's own name, here 'textstats', in every file that is imported. The guarded self-check of textstats.py is skipped for that reason.

Each form of import binds a different name

All five forms find and load the module the same way; they differ only in the names they bind in your file. With textstats from the example:

Statement What it binds in your file Then you write
import textstats the module, as textstats textstats.word_count(line)
import textstats as ts the module, as ts ts.word_count(line)
from textstats import word_count one name of the module word_count(line)
from textstats import word_count as count that name, as count count(line)
from textstats import * every public name of the module names whose origin the reader cannot see
  • import textstats keeps the source visible at every use: textstats.word_count tells a reader where the function lives. It suits a module you use in many places.
  • from textstats import word_count is shorter for a few names you use often. It copies the reference when the import runs, so if the module later binds that name to something new, your file keeps the old object.
  • as gives a shorter or clearer name. Use it for long module names and for aliases that readers of your code already know.
  • A star import: from module import *, which binds every public name of a module in your file: the names in its __all__ list if it has one, otherwise every name that does not start with an underscore. It can silently replace names you already have., from module import *, binds every public name: the names listed in the module’s __all__ if it has that list, otherwise every name that does not start with an underscore. It can silently replace names you already have, built-in ones included:
A star import replaces open() Python · star/star_import.py
from os import *  # every public name of os, including its own open()

print("open now comes from", open.__module__)
try:
    with open("notes.txt") as notes:  # meant the built-in open()
        print(notes.read())
except TypeError as error:
    print("TypeError:", error)

Output

open now comes from posix
TypeError: open() missing required argument 'flags' (pos 2)

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 star_import.py

The os module has a low-level open() of its own, which needs flags such as os.O_RDONLY, and the star import put it in the place of the built-in open(); the first line names posix, the module that os takes it from on Linux and macOS. The error talks about a missing flags argument and says nothing about the import that caused it, which is the real cost of *: the bug and its cause sit far apart. Keep star imports for quick experiments at the interactive prompt.

Checkpoint

Check: What each form of import binds

  1. Question 1 of 2 What does this print?

    Read the code, then choose one answer.

    import math
    from math import pi
    
    math.pi = 3
    print(pi, math.pi)
    A hint

    Think about what the name pi in your file refers to after the from import ran.

    Show the answer: Question 1 of 2

    Answer: 3.141592653589793 3

    from math import pi copies the reference when the import runs, so your pi keeps the float it got. math.pi = 3 rebinds the name inside the module, which only code that writes math.pi sees. That is the difference the lesson describes between import m with m.name and from m import name.

  2. Question 2 of 2 textstats.py defines PUNCTUATION, words(), word_count() and most_common(), and has no __all__ list. Which names does from textstats import * bind?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: PUNCTUATION, words, word_count and most_common

    A star import binds the names of the module's __all__ list when it has one, otherwise every name that does not start with an underscore. For textstats that is PUNCTUATION, words, word_count and most_common, and any of them replaces a name of yours that is spelled the same.

Python takes the first match on sys.path

When a module is not in sys.modules yet, Python checks the modules built into the interpreter itself (their names are in sys.builtin_module_names) and then searches the folders of the module search path: The list sys.path of folders that Python searches, in order, for a module it has not imported yet. For a script it starts with the script's folder, then the standard library, then site-packages, and the first match wins., sys.path, in order, taking the first match: a file textstats.py, a package folder textstats/ or a compiled extension module. This program says what each entry of sys.path is for:

What is on sys.path? Python · path/where.py
import os
import sys

script_folder = os.path.dirname(os.path.abspath(__file__))


def describe(entry):
    """What one folder of the module search path is for."""
    if entry == "":
        return "'' (empty): the current folder"
    if entry == script_folder:
        return "the folder of the script that is running"
    if entry.endswith(".zip"):
        state = "present" if os.path.exists(entry) else "absent here, so it is skipped"
        return f"a zip file of the standard library ({state})"
    if entry.endswith("lib-dynload"):
        return "lib-dynload: standard modules compiled from C"
    if entry.endswith("site-packages"):
        return "site-packages: packages you install"
    return "the standard library"


for number, entry in enumerate(sys.path, start=1):
    print(number, describe(entry))

Output

1 the folder of the script that is running
2 a zip file of the standard library (absent here, so it is skipped)
3 the standard library
4 lib-dynload: standard modules compiled from C
5 site-packages: packages you install

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 where.py

Version note

In the browser runner the first entry is an empty string, which means the current folder (where the page puts the files of the example), and the standard library is read from the zip file, which exists there. Run as a file on your computer, a script gets its own folder as the first entry instead.

In your browser, Pyodide 314.0.7 (CPython 3.14.2) prints:

1 '' (empty): the current folder
2 a zip file of the standard library (present)
3 lib-dynload: standard modules compiled from C
4 site-packages: packages you install
  • First, the script’s folder. The first entry is the folder that holds the script you ran, or the current folder for python -m, python -c and the interactive prompt. That is how main.py finds textstats.py next to it.
  • Then the standard library. Python lists a zip file of the standard library even when there is none (it is skipped), then the library’s folder and lib-dynload, its modules compiled from C.
  • Last, site-packages. That is where pip installs packages: the environment’s own folder when a virtual environment is active. The PYTHONPATH environment variable, when set, adds folders after the first entry.

The flowchart puts a whole import together: the cache first, then the search in this order, then one run of the file and the name bound in your file.

Flowchart of an import: check sys.modules, else search built-ins and sys.path; create, cache and run the module once; bind the name.import textstatsAlready insys.modules?Search in order:1. built-in modules2. each folder of sys.path(your script's folder,the standard library,site-packages)Found?ModuleNotFoundErrorCreate the module,add it to sys.modules,run the file once,top to bottomBind the nametextstatsin your fileyes: reuse itnonoyes

What Python does for import textstats

Text description of the diagram

The flowchart reads from top to bottom.

  • It starts at the statement import textstats.
  • First question: is textstats already in sys.modules, the cache of modules this program has imported? If yes, Python reuses that module object and goes straight to the last step.
  • If not, Python searches in order: the built-in modules, then each folder listed in sys.path, which starts with the folder of your script, then the standard library, then site-packages.
  • Second question: was a module of that name found? If not, the import raises ModuleNotFoundError.
  • If it was found, Python creates a module object, adds it to sys.modules and runs the file once, from top to bottom.
  • Last step: the name textstats is bound in the importing file, pointing at the module object.

Checkpoint

Check: The order of sys.path

  1. One question You run python tools/report.py from your home folder. What is the first entry of sys.path?

    Choose one answer.

    Show the answer: One question

    Answer: The tools folder, which holds report.py

    The first entry of sys.path is the folder of the script that is running, here tools/. That is how report.py finds modules that sit beside it, and why a file there can hide a module further down the list.

A file named like a standard module hides it

Because the script’s folder comes first, a file of your own with the name of a standard module wins over it, and the standard module becomes a shadowed module: A standard module that an import cannot reach because a file or package of yours with the same name comes earlier on sys.path, such as your own statistics.py next to the script that imports statistics.. Here a folder holds statistics.py, written for one project’s marks, and report.py, which wants the standard library’s statistics.median:

A file called statistics.py

Python · clash/report.py

import statistics

marks = [72, 85, 64, 91]
print("Median mark:", statistics.median(marks))

Output (exit status 1)

Printed as an error (standard error)

Traceback (most recent call last):
  File "report.py", line 4, in <module>
    print("Median mark:", statistics.median(marks))
                          ^^^^^^^^^^^^^^^^^
AttributeError: module 'statistics' has no attribute 'median' (consider renaming 'statistics.py' since it has the same name as the standard library module named 'statistics' and prevents importing that standard library module)

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 report.py

clash/statistics.py

"""My own helpers for marks. Its name hides the standard library's statistics module."""


def average(marks):
    return sum(marks) / len(marks)

Step through the search that line 1 of report.py made, and watch where it stopped:

Interactive · stage by stage How import statistics finds the wrong file Step through the search of line 1 of report.py. The search stops at the first match, so the order of the folders decides which file runs.
How import statistics finds the wrong file, step 7 of 7. Showing import statistics, In sys.modules?, Built into Python?, The script's folder (no statistics.py now), That file runs once (the standard statistics), statistics.median(marks) (median() works). In focus: The script's folder.rename the fileimport statisticsline 1 of report.pyIn sys.modules?not imported yetBuilt into Python?not a built-in moduleThe script's folderno statistics.py nowThat file runs oncethe standard statisticsstatistics.median(marks)median() works

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. The statement. report.py starts with import statistics. It means the standard module, but the import statement only knows a name, and Python looks for the first module of that name.
  2. Not cached yet. Nothing in this program has imported statistics yet, and Python does not load it while it starts, so sys.modules has no entry of that name and the search begins.
  3. Not built in. Python first checks the modules compiled into the interpreter itself, whose names are in sys.builtin_module_names. statistics is not one of them: the standard module is a file in the library folder.
  4. The first folder matches. The first folder on sys.path is the script's own folder, and it holds statistics.py, written for one project's marks. That is a match, and the search stops: the standard library's folder, further down the list, is never searched.
  5. The wrong file runs. Python creates the module statistics from the local file, puts it into sys.modules and runs it once. It defines average() and nothing else.
  6. The missing name. Line 4 asks the module for median, and this module has none, so the program stops with AttributeError: module 'statistics' has no attribute 'median'. Python 3.13 and newer add a hint that names the file to rename.
  7. The fix. Rename the local file after its job, such as mark_stats.py. The script's folder then has no statistics.py, the search goes on to the standard library, and statistics.median() is the real one.

import statistics found the local file first, and it has no median. Python 3.13 and newer notice the clash and name the file to rename, as the end of the message shows; the lesson on tracebacks meets the same mistake with random.py. Older versions only say that the attribute is missing. Three habits prevent it:

  • Name files after what they do in your project (mark_stats.py, dice_game.py), not after a general topic.
  • Check a name against sys.stdlib_module_names, the set of every standard module name. The program below checks a whole folder:
Which files hide a standard module? Python · clash/check_names.py
"""List the files in this folder that would hide a module of the standard library."""
import sys
from pathlib import Path

for path in sorted(Path(".").iterdir()):
    if path.suffix == ".py":
        name = path.stem
    elif path.is_dir():
        name = path.name
    else:
        continue
    if name in sys.stdlib_module_names:
        print(f"{path.name} hides the standard library module {name!r}")
print(len(sys.stdlib_module_names), "names belong to the standard library")

Output

statistics.py hides the standard library module 'statistics'
297 names belong to the standard library

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 check_names.py

  • Run a single-file tool with python -P, which leaves the script’s folder off sys.path: python -P report.py gets the real statistics. The catch is that the script can no longer import other files beside it, so this suits standalone tools better than projects.

Not every standard module can be hidden this way. Python imports sys, os and a few others while it starts, before your first line runs, and an import that finds a module in sys.modules never searches the folders. A local os.py is therefore ignored, while a local statistics.py, json.py or random.py takes over.

Checkpoint

Check: A file that hides a standard module

  1. One question A folder holds report.py, which needs the standard statistics.median(), and a file of your own called statistics.py. Which command runs report.py with the standard module, without renaming anything?

    Choose one answer.

    Show the answer: One question

    Answer: python -P report.py

    The local file wins because its folder comes first on sys.path. python -P report.py leaves that folder off, so import statistics finds the standard module. The catch is that report.py can no longer import other files beside it, which is why renaming the file is the usual fix in a project.

Two modules that import each other meet half-built

A circular import: Two or more modules that import each other. It fails when one of them asks for a name that the other has not defined yet, because that module is still running its first lines. happens when two modules each need a name from the other while they are still loading. orders.py needs discount_for from discounts.py, and discounts.py needs TAX_RATE from orders.py:

orders.py and discounts.py import each other

Python · circular/main.py

import orders

print("Total:", orders.total(1500))

Output (exit status 1)

Printed as an error (standard error)

Traceback (most recent call last):
  File "main.py", line 1, in <module>
    import orders
  File "orders.py", line 1, in <module>
    from discounts import discount_for
  File "discounts.py", line 1, in <module>
    from orders import TAX_RATE
ImportError: cannot import name 'TAX_RATE' from 'orders' (consider renaming 'orders.py' if it has the same name as a library you intended to import)

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 main.py

circular/orders.py

from discounts import discount_for

TAX_RATE = 0.18


def total(price):
    """The price after the discount, with tax added."""
    return round((price - discount_for(price)) * (1 + TAX_RATE), 2)

circular/discounts.py

from orders import TAX_RATE


def discount_for(price):
    """10 % off when the price with tax is over 1,000."""
    return price * 0.10 if price * (1 + TAX_RATE) > 1000 else 0

The traceback lists three first lines, one under the other. Step through them in the order they ran, and watch how far orders.py has got when discounts.py asks it for TAX_RATE:

Interactive · message by message orders and discounts import each other Step through the first lines of the three files, in the order they run. The sub-labels say how far each file has got.
orders and discounts import each other, step 6 of 6. Messages so far: main.py to orders.py: import orders; orders.py to discounts.py: import discounts; discounts.py to orders.py: TAX_RATE?; orders.py to discounts.py: ImportError.main.pythe programorders.pyloads to its enddiscounts.pyTAX_RATE from rates.pyimport ordersimport discountsTAX_RATE?ImportError

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. main.py imports orders. Line 1 of main.py is import orders. Python finds orders.py, creates the module orders, adds it to sys.modules and starts running the file.
  2. orders needs discounts. Line 1 of orders.py is from discounts import discount_for. discounts is not loaded yet, so Python creates it, adds it to sys.modules and starts discounts.py, while orders waits on its first line.
  3. discounts needs orders. Line 1 of discounts.py is from orders import TAX_RATE. orders is already in sys.modules, so Python does not load it a second time. It looks for TAX_RATE in the module that exists.
  4. The name is not there yet. orders.py is still on line 1, waiting for discounts, and TAX_RATE = 0.18 is on line 3, which has not run. The module exists, but the name does not.
  5. ImportError. So the from import fails with ImportError: cannot import name 'TAX_RATE' from 'orders'. The traceback lists the three first lines, one under the other, which is the sign of a cycle.
  6. The fixed version. In the fixed files TAX_RATE lives in rates.py, which imports nothing of the project. discounts and orders both import it from there, every file loads to its end, and main.py prints Total: 1593.0 and Total: 944.0.

Read the traceback from the top: main.py imports orders, whose first line imports discounts, whose first line asks orders for TAX_RATE. But orders.py has not got that far. It is still on its first line, waiting for discounts to finish. The module object already exists, because Python adds a module to sys.modules just before it runs the file, but TAX_RATE is not defined in it yet, so the import fails.

The message does not help much here. For a module in the script’s own folder, Python 3.13 and newer add the hint about renaming a file that hides a library, which is not the problem. Before Python 3.13 the same failure mentioned a “partially initialized module” and a likely circular import, and for modules inside a package it still does. When a name that plainly exists cannot be imported, look for a cycle. There are three ways out:

  • Move what both need into a third module. TAX_RATE goes into rates.py, which imports neither of the others, and the cycle is gone. This is usually the best fix, because the shared value had no clear home before.
  • Import the module instead of the name. With import orders at the top of discounts.py and orders.TAX_RATE inside the function, the name is looked up when the function runs, long after both files have loaded.
  • Import inside the function that needs the name. It works, but it hides a dependency at the place nobody looks.
TAX_RATE moved into rates.py

Python · fixed/main.py

import orders

print("Total:", orders.total(1500))
print("Total:", orders.total(800))

Output

Total: 1593.0
Total: 944.0

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 main.py

fixed/rates.py

TAX_RATE = 0.18

fixed/orders.py

from discounts import discount_for
from rates import TAX_RATE


def total(price):
    """The price after the discount, with tax added."""
    return round((price - discount_for(price)) * (1 + TAX_RATE), 2)

fixed/discounts.py

from rates import TAX_RATE


def discount_for(price):
    """10 % off when the price with tax is over 1,000."""
    return price * 0.10 if price * (1 + TAX_RATE) > 1000 else 0

With TAX_RATE in rates.py, every file loads to its end and both totals print: 1593.0 is the 1,500 order with 10 % off and 18 % tax added, and 944.0 is the 800 order with tax and no discount, because it stays under 1,000.

Checkpoint

Check: Modules that import each other

  1. One question In the circular example, orders.py does define TAX_RATE. Why does from orders import TAX_RATE in discounts.py still fail?

    Choose one answer.

    A hint

    Which line of orders.py was running when discounts.py asked for the name?

    Show the answer: One question

    Answer: orders.py is still on its first line, so the line that defines TAX_RATE has not run yet

    main.py imports orders, whose first line starts discounts, whose first line asks orders for TAX_RATE. orders is in sys.modules but has not got past its first line, so TAX_RATE does not exist yet and the import fails.

The guard keeps a script quiet when it is imported

The main guard: The block under if __name__ == "__main__": in a file. It runs when the file is started as the program and is skipped when the file is imported, because __name__ then holds the module's own name., if __name__ == "__main__":, matters whenever something imports your file without you typing import yourself. Test runners import modules to call their functions. And multiprocessing runs your main file again in a new Python process, under the name '__mp_main__', so that the code there can find your functions. Its spawn start method, the default on Windows and macOS, does that in every worker it starts; forkserver, the default on Linux since Python 3.14, does it once, in the server process from which it forks the workers. This example uses spawn:

A worker process imports the main file Python · workers/workers.py
import multiprocessing as mp

print(f"{mp.current_process().name} runs this file, __name__ = {__name__!r}", flush=True)


def square(n):
    print(f"{mp.current_process().name} works out {n} * {n} = {n * n}", flush=True)


if __name__ == "__main__":
    # "spawn" starts each worker as a fresh Python that imports this file first
    # (the default on Windows and macOS).
    ctx = mp.get_context("spawn")
    worker = ctx.Process(target=square, args=(7,))
    worker.start()
    worker.join()
    print("The worker finished with exit code", worker.exitcode)

Output

MainProcess runs this file, __name__ = '__main__'
SpawnProcess-1 runs this file, __name__ = '__mp_main__'
SpawnProcess-1 works out 7 * 7 = 49
The worker finished with exit code 0

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 workers.py

Four lines, from two processes. Step through the run process by process, and watch the guard give a different answer in each:

Interactive · message by message A spawned worker imports the main file again Step through the run of workers.py. The sub-labels say which __name__ each process gave the file.
A spawned worker imports the main file again, step 7 of 7. Messages so far: Main process to SpawnProcess-1: run square(7); SpawnProcess-1 to Main process: exit code 0.Main process__name__ is '__main__'SpawnProcess-1square(7) printed 49run square(7)exit code 0

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. The program starts. You run python workers.py. The file is the program, so its __name__ is '__main__', and its top-level print() gives the first line of the output.
  2. The guard is true. Python reaches if __name__ == "__main__":, which is true here. The block chooses the spawn start method and starts one worker process to run square(7).
  3. A fresh Python. Spawn starts a new Python interpreter. To call square() it needs the function, so it runs workers.py again from the top, this time under the name '__mp_main__'.
  4. The top level runs again. The worker's run of the top level prints the second line of the output, with its own process name, SpawnProcess-1, and __name__ = '__mp_main__'.
  5. The guard is false. In the worker, __name__ is not '__main__', so the guarded block is skipped and the worker starts no worker of its own. Without the guard it would try, and multiprocessing would stop it with a RuntimeError.
  6. The work itself. Only now does the worker call square(7), which prints the third line: SpawnProcess-1 works out 7 * 7 = 49.
  7. Back in the main process. The worker ends with exit code 0. worker.join() returns in the main process, which prints the last line: The worker finished with exit code 0.

The worker ran the top level of workers.py again, as its first line of output shows, but its __name__ was '__mp_main__', so the guard kept it from starting a worker of its own. Without the guard, every new process would try to start more processes while it was still importing the file, and multiprocessing stops that with a RuntimeError, as its documentation explains under “Safe importing of main module”.

Keep the guarded block short. Put the program’s work in a function, usually called main(), and make the guard a single line, sys.exit(main()): main() returns 0 for success or another number for failure, and sys.exit() hands it to the shell as the exit status. The exercise below turns a script that prints a report on import into one that does this.

Version note

Python 3.15 adds explicit lazy imports (PEP 810): lazy import json binds the name at once but runs the module only when the name is first used. Programs that import many modules start faster with it. The module of this track on Python 3.15 covers it.

Checkpoint

Check: __name__ and the main guard

  1. Question 1 of 2 With the spawn start method, workers.py starts one worker. How many times does its top-level print() run, counting both processes?

    Type a number.

    A hint

    The worker needs the file's functions before it can call one.

    Show the answer: Question 1 of 2

    Answer: 2 times

    The main process runs the file as the program, and the spawned worker runs it again, under the name '__mp_main__', to get the function it must call. So the top-level print() runs twice, once in each process, while the guarded block runs only in the main process.

  2. Question 2 of 2 What is __name__ inside workers.py while the spawned worker runs it?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: '__mp_main__'

    The worker runs workers.py under the name '__mp_main__', as the second line of the output shows. The test __name__ == "__main__" is therefore false in the worker, and the guarded block that starts workers runs only in the main process.

Python Online Compiler Try the import forms of this lesson on standard modules, for example from statistics import median, and print each module's __name__.

Practise: make a script safe to import

tally.py counts the votes of a class election, and importing it prints a whole report. Turn it into a module that stays quiet when it is imported and a script that still prints the report, with a main() function and sys.exit(main()) under the guard. The sample tests import your file and also run it as a program.

Exercise · Easy · Python

Make a script safe to import

tally.py counts the votes of a class election. Its functions are useful elsewhere, but importing the file also prints a whole report, because the demo code sits at the top level. Change the file so that it works both ways:

  • Importing tally prints nothing.
  • A function main(votes=None) prints the report and returns an exit status. With no argument it counts the demo votes already in the file. The report has one line per name in alphabetical order, such as Asha: 3, then a line Winner: Asha, and main returns 0.
  • When no vote has a name in it (every vote is empty or only spaces), main prints No votes and returns 1.
  • Running the file as a program (python tally.py) prints the report for the demo votes and exits with the status that main returns: call it as sys.exit(main()) inside an if __name__ == "__main__": block.

Keep count_votes and winner as they are. The sample tests import your file as tally.

Starter code · tally.py

"""Count the votes of a class election and print the result."""

DEMO_VOTES = ["asha", "Ravi", " asha ", "meera", "ravi", "Asha", "  "]


def count_votes(votes):
    """A dict of name -> number of votes; names are tidied with strip() and title(), empty votes are skipped."""
    counts = {}
    for vote in votes:
        name = vote.strip().title()
        if name:
            counts[name] = counts.get(name, 0) + 1
    return counts


def winner(counts):
    """The name with the most votes (on a tie, the name that comes first alphabetically)."""
    return min(counts, key=lambda name: (-counts[name], name))


# Demo: it runs every time the file is imported, too.
counts = count_votes(DEMO_VOTES)
for name in sorted(counts):
    print(f"{name}: {counts[name]}")
print("Winner:", winner(counts))
The sample tests · test_tally.py
import contextlib
import importlib
import io
import runpy

import tally


def output_of(action):
    """What action() prints, and what it returns."""
    buffer = io.StringIO()
    with contextlib.redirect_stdout(buffer):
        result = action()
    return buffer.getvalue(), result


def test_import_is_quiet():
    """importing tally prints nothing"""
    printed, _ = output_of(lambda: importlib.reload(tally))
    assert printed == ""


def test_count_votes():
    """count_votes still tidies the names and skips empty votes"""
    assert tally.count_votes(["ravi", " RAVI", "", "meera"]) == {"Ravi": 2, "Meera": 1}


def test_main_report():
    """main(votes) prints one line per name, then the winner, and returns 0"""
    printed, status = output_of(lambda: tally.main(["meera", "Ravi", "ravi"]))
    assert printed == "Meera: 1\nRavi: 2\nWinner: Ravi\n"
    assert status == 0


def test_main_tie():
    """on a tie the winner comes first alphabetically"""
    printed, status = output_of(lambda: tally.main(["zoya", "Arjun"]))
    assert printed == "Arjun: 1\nZoya: 1\nWinner: Arjun\n"
    assert status == 0


def test_main_no_votes():
    """with no named vote, main prints No votes and returns 1"""
    printed, status = output_of(lambda: tally.main(["", "   "]))
    assert printed == "No votes\n"
    assert status == 1


def test_run_as_program():
    """python tally.py prints the demo report and exits with status 0"""
    buffer = io.StringIO()
    code = None
    with contextlib.redirect_stdout(buffer):
        try:
            runpy.run_path("tally.py", run_name="__main__")
        except SystemExit as stop:
            code = stop.code
    assert buffer.getvalue() == "Asha: 3\nMeera: 1\nRavi: 2\nWinner: Asha\n"
    assert code == 0
A hint

Move the four demo lines into the body of def main(votes=None): and replace the demo list with votes when it is not None. Check if not counts: before you look for a winner, because winner() cannot pick from an empty dictionary. Then add the guard as the last lines of the file, with import sys at the top.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

The self-check below covers the six ideas of this lesson, from the module cache to the main guard. It starts with the idea your answers show you know least, and every choice says why it is right or what it gets wrong:

Practice that adapts to you

Practise this lesson

16 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 16 main.py imports textstats with three different statements. What does it print?

    What does this program print? Choose one answer.

    import sys
    
    import textstats
    import textstats as ts
    from textstats import word_count
    
    line = "Practice makes progress, and progress makes practice easier."
    print(word_count(line), "words")
    print("Most common:", textstats.most_common(line))
    print("One module object?", ts is textstats is sys.modules["textstats"])
    print("Its name when imported:", repr(textstats.__name__))
    Show the answer: Question 1 of 16

    Answer: it prints

    textstats.py is running, and its __name__ is 'textstats'
    8 words
    Most common: practice
    One module object? True
    Its name when imported: 'textstats'

    The first import runs textstats.py once and caches the module in sys.modules; the next two find it there and only bind a name, so the line from textstats.py appears once and all three names point at one object. An imported file gets its module name, 'textstats', as __name__, so its guarded self-check does not run.

  2. Question 2 of 16 What is sys.modules?

    Choose one answer.

    Show the answer: Question 2 of 16

    Answer: A dictionary from the name of every module imported so far to its module object

    sys.modules maps each module name to the module object built by its first import. Because every import checks it first, a module's file runs once per program, and ts is textstats is sys.modules["textstats"] is True.

  3. Question 3 of 16 At the interactive prompt you run import textstats, then change textstats.py in an editor and save it. You type import textstats again. Which code does the prompt now use?

    Choose one answer.

    A hint

    The prompt is one program that is still running.

    Show the answer: Question 3 of 16

    Answer: The old code, because textstats is still in sys.modules and the file is not run again

    The interactive prompt is a single program, so its sys.modules keeps the module from the first import, and a second import statement reuses it without reading the file. To use the edited file, start the prompt again.

  4. Question 4 of 16 What happens when this runs?

    Read the code, then choose one answer.

    from os import *
    
    with open("notes.txt") as notes:
        print(notes.read())
    Show the answer: Question 4 of 16

    Answer: TypeError, because the star import replaced the built-in open() with os.open(), which needs a flags argument

    A star import binds every public name of the module in your file, and os has a function called open. It takes the place of the built-in open(), and os.open() needs flags such as os.O_RDONLY, so the call fails before any file is read. That is why star imports are best left to the interactive prompt.

  5. Question 5 of 16 A file contains only one import, import textstats as ts. Which call works?

    Choose one answer.

    Show the answer: Question 5 of 16

    Answer: ts.word_count(line)

    import textstats as ts imports the module once and binds it under the name ts, so every use is qualified with ts, as in ts.word_count(line). Neither textstats nor word_count becomes a name of the file.

  6. Question 6 of 16 python main.py runs import textstats for the first time. Put the places Python looks in order, first one first.

    Give each item its position, from 1 (first).

    Show the answer: Question 6 of 16

    Answer:

    1. sys.modules, the modules already imported
    2. the modules built into the interpreter
    3. the folder that holds main.py
    4. the folders of the standard library
    5. site-packages, where pip installs packages

    An import checks the module cache first. When the module is not there, Python checks the modules built into the interpreter, then searches the folders of sys.path in order: the script's folder, the standard library and lib-dynload, then site-packages. The first match wins.

  7. Question 7 of 16 PYTHONPATH is set to /home/asha/libs and you run python main.py. Where does that folder appear in sys.path?

    Choose one answer.

    Show the answer: Question 7 of 16

    Answer: Right after the first entry, the folder of main.py, and before the standard library

    For python main.py, sys.path starts with the folder of main.py; the folders of PYTHONPATH, when it is set, come next, then the standard library and site-packages. A module in /home/asha/libs is therefore found before a standard module of the same name.

  8. Question 8 of 16 Your project folder holds report.py and a file of your own called json.py. You run python report.py, and it starts with import json. Which module does it get?

    Choose one answer.

    Show the answer: Question 8 of 16

    Answer: Your json.py, because the folder of the script comes first in sys.path

    The first entry of sys.path is the folder that holds the script, and Python takes the first match it finds, so your json.py hides the standard module. Rename your file. Modules that Python imported before your code started, such as sys and os, come from sys.modules instead and are not hidden this way.

  9. Question 9 of 16 You want to name a file of your project secrets.py. Which test tells you whether it would hide a standard module?

    Choose one answer.

    Show the answer: Question 9 of 16

    Answer: "secrets" in sys.stdlib_module_names

    sys.stdlib_module_names is the set of every standard module name, which is what check_names.py compares the files of a folder with. "secrets" is in it, so a secrets.py of your own would hide the standard module.

  10. Question 10 of 16 A folder holds a script and two files of your own, os.py and json.py. The script imports os and json. Which of your files takes the place of a standard module?

    Choose one answer.

    A hint

    Which of the two modules has Python already imported before your first line runs?

    Show the answer: Question 10 of 16

    Answer: Only json.py, because os was imported while Python started and comes from sys.modules

    Python imports sys, os and a few others while it starts, before the script's first line, so import os finds os in sys.modules and never searches the folders. json is not loaded at start-up, so import json searches sys.path and finds your json.py first.

  11. Question 11 of 16 orders.py starts with from discounts import discount_for and discounts.py starts with from orders import TAX_RATE. Running the program fails with an ImportError. Which change is the cleanest fix?

    Choose one answer.

    Show the answer: Question 11 of 16

    Answer: Move TAX_RATE into a third module that both files import

    Each module needs a name of the other while it is still running its first lines, so whichever starts first finds the other half-built. A third module that imports neither of them breaks the cycle. Importing inside the function that needs the name also works, but it hides the dependency.

  12. Question 12 of 16 When does Python add a module that it is loading to sys.modules?

    Choose one answer.

    Show the answer: Question 12 of 16

    Answer: Just before it runs the module's file

    Python creates the module object and adds it to sys.modules just before it runs the file. That is why two modules that import each other see each other half-built: the module exists, but its later lines have not run.

  13. Question 13 of 16 Without a third module, which change to discounts.py also breaks the cycle with orders.py?

    Choose one answer.

    A hint

    Which kind of import needs nothing more than the module object?

    Show the answer: Question 13 of 16

    Answer: Write import orders at the top and use orders.TAX_RATE inside discount_for()

    An import of the module needs only the module object, which is in sys.modules from the start. Reading the name later, inside the function, happens long after both files have loaded. Moving the shared value into a third module is still the clearer fix.

  14. Question 14 of 16 A file ends with if __name__ == "__main__": main(). Another file imports it. What happens to that block?

    Choose one answer.

    Show the answer: Question 14 of 16

    Answer: It does not run, because __name__ is the module's own name when the file is imported

    An imported module's __name__ is its module name, such as 'textstats', so the comparison is false and the block is skipped. The rest of the top level still runs on import, which is why demo code belongs inside the guard.

  15. Question 15 of 16 A test imports tally.py to call count_votes(), and every import prints a whole report. What change keeps the report for python tally.py and stops it on import?

    Choose one answer.

    Show the answer: Question 15 of 16

    Answer: Put the report in a main() function and call it under if __name__ == "__main__":

    Everything at the top level runs on every import. Moving the report into main() and calling it under the guard makes it run only when tally.py is the program, which is exactly what the exercise of this lesson asks for.

  16. Question 16 of 16 A program uses multiprocessing with its default start method on Linux, under Python 3.14. Does its main file need the guard?

    Choose one answer.

    Show the answer: Question 16 of 16

    Answer: Yes, because the default there is now forkserver, which imports the main file in its server process

    Spawn, the default on Windows and macOS, imports the main file in every worker, and forkserver, the default on Linux since Python 3.14, imports it once in its server process. With either one, code that starts processes must sit under the guard.

Interview questions

Fresher · What does if __name__ == "__main__": do, and when does a script need it?

Every module has its name in __name__. Python sets it to "__main__" in the file you start as the program, and to the module’s own name, such as "textstats", when the file is imported, so the guarded block runs only when the file is the program. A script needs the guard as soon as anything else may import it: a test that calls its functions, another module that reuses them, or multiprocessing, whose spawn and forkserver start methods (the defaults on every platform since Python 3.14) import the main file again in a new process. Without the guard, that import would run the demo code or try to start more processes, which multiprocessing refuses with a RuntimeError. The usual shape is a main() function that does the work and sys.exit(main()) as the only line under the guard.

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher to mid

    A report script works on your laptop, but on the build server the same commit fails with "AttributeError: module 'statistics' has no attribute 'median'". Nobody changed the script. Explain the likely cause, how you would prove it in a minute, and how you would stop it from happening again.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    In a service package, billing.py and customers.py import each other, and it has worked for months. After someone sorts the imports in main.py, start-up fails with "cannot import name 'Invoice' from partially initialized module 'service.billing'". Explain why, which fixes you would consider, and which one you would choose.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • A module is a .py file that Python runs once per program, on the first import, and caches in sys.modules, so every later import reuses the same module object.
  • import m keeps names qualified (m.f), from m import f binds one name, as renames, and from m import * can replace your names and built-ins, as from os import * does to open().
  • sys.path starts with the script’s folder, then the standard library, then site-packages, and Python takes the first match.
  • A statistics.py or json.py of your own therefore hides the standard module: name files after their job and check names against sys.stdlib_module_names.
  • A circular import fails when two modules need each other’s names while loading, so move the shared part into a third module, or import the module instead of the name.
  • __name__ is '__main__' in the program you started and the module’s name when imported, so put demo code in main() and call it under if __name__ == "__main__":.

Terms in this lesson

circular import
Two or more modules that import each other. It fails when one of them asks for a name that the other has not defined yet, because that module is still running its first lines.
main guard
The block under if __name__ == "__main__": in a file. It runs when the file is started as the program and is skipped when the file is imported, because __name__ then holds the module's own name.
module
A file of Python code, such as textstats.py, that other files use with import. The first import in a program runs the file once and keeps the module object it built, so every later import gets that same object.
module cache
The dictionary sys.modules, which maps the name of every module a program has imported to its module object. An import looks there first, which is why a module's file runs only once per program.
module search path
The list sys.path of folders that Python searches, in order, for a module it has not imported yet. For a script it starts with the script's folder, then the standard library, then site-packages, and the first match wins.
shadowed module
A standard module that an import cannot reach because a file or package of yours with the same name comes earlier on sys.path, such as your own statistics.py next to the script that imports statistics.
star import
from module import *, which binds every public name of a module in your file: the names in its __all__ list if it has one, otherwise every name that does not start with an underscore. It can silently replace names you already have.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.