Creating and Organizing Modules
An import statement tells Python to find a module file, execute its code, and make its contents available to your program.
Try it: What import Really Does
What happens on import: Python checks sys.modules, searches sys.path in order (first match wins), creates the module object, runs its top level once, then binds a name — the module, one function, or an alias — in the importing namespace; plus packages, __name__ == "__main__", ModuleNotFoundError and ImportError.
How it works
- If the name is already in sys.modules, reuse that module object: its code does not run again.
- Otherwise search each sys.path entry in order (the script's folder first); the first match wins, none raises ModuleNotFoundError.
- Create an empty module object, store it in sys.modules, then run the file's top level once (its __name__ is the module name, so a __main__ guard is skipped).
- import m binds m to the module; import m as x binds x; from m import f binds only f (ImportError if m has no f).
- A package runs its __init__.py first; submodules are found on the package's __path__ and become attributes of the package.
Default run (19 steps): $ python main.py — sys.path starts as [/project, /venv/site-packages, /usr/lib/python3.12]: the script's folder, then site-packages, then the standard library. Only our own modules are shown in sys.modules below. … Done. Printed: helpers loaded, __name__ = helpers | Hi Ann | Hi Ann.
Simplified: A fixed virtual project (main.py, helpers.py, pkg/, a site-packages folder and a one-file stand-in for the standard library). Built-in and frozen modules, .pyc caching, relative imports and namespace packages are not modelled; only our own modules are listed in sys.modules.
Loading the simulation…
The Hidden Work Behind import
An import statement is an instruction to Python to locate a module, load it, execute its code, and make the module's contents available to the importing program. It is not merely a variable declaration or a function definition. Writing an import starts a search-and-execute process that Python performs behind the scenes.
Following the Search Path
For a module that is not built into Python, the interpreter uses a list called sys.path. This list contains directories, and Python checks those directories in a specific order for a file matching the requested module name. The search stops as soon as Python finds the first match.
A Module Name Appears in Two Directories
Suppose Python is asked to import a non-built-in module named settings, and two directories in sys.path contain a matching module file.
- 1Start with sys.path
Python begins with the first directory listed in
sys.path. - 2Check directory order
Python checks each directory in the order provided by
sys.path. - 3Stop at the first match
If the requested module is found in an earlier directory, Python uses that file and does not continue to a later matching file.
- 4Recognize the naming risk
The selected file depends on the search order, so a file with the same name as another module can create a naming conflict.
The first matching module file in sys.path controls which module Python loads.
First Import and Later Imports
When Python finds a module, it executes the code in the module's body during initialization. That initialization happens only once, the first time the module is imported. Python then caches the loaded module in memory. If the same module is imported again later, Python reuses the cached module instead of executing its initialization code again.
A State-Holding Module
Imagine a module that creates a counter during its initialization and changes that counter while the program runs. The program imports the module once and later imports the same module again.
- 1Initial import
Python locates the module, executes its module-body code, and creates the module's variables and functions.
- 2Module becomes cached
After initialization, Python keeps the loaded module in memory.
- 3State changes
The module's counter or other state can change while the program executes.
- 4Second import
Python returns the cached module instead of re-running its initialization code.
The module's existing state persists because later imports reuse the initialized module.
Using Module Contents
After Python locates and executes a module file, the module's contents become available to the importing program. Those contents can include the module's variables, functions, and other names. The important sequence is locate, load, execute, and then make the contents available. The importing program is therefore using a module that Python has initialized, rather than simply receiving an undeclared collection of names.
Consider a program that imports a module containing configuration data. The first import causes Python to locate and initialize that module. Later parts of the program can use the module's contents, and later imports reuse the initialized module rather than creating a newly initialized copy.
Mistakes with Module Search
Common mistake Assuming Python searches every possible location at the same time.
What it looks likeTreating all directories as equally likely sources for a requested module.
Why it failsPython checks directories listed in
sys.pathin a specific order and stops at the first match.Do this insteadReason from the ordered entries in sys.path and identify which directory is checked first.
Common mistake Expecting a second import to run the module's initialization code again.
What it looks likeExpecting a module's initial state to be recreated whenever another part of the program imports it.
Why it failsPython caches the module after the first import and reuses it for later imports.
Do this insteadExpect later imports to reuse the initialized module and preserve its state during the program's execution.
Common mistake Assuming an import only retrieves something already present.
What it looks likeThinking of import as equivalent to declaring a variable.
Why it failsAn import can trigger a search, file loading, code execution, and exposure of module contents.
Do this insteadTrace the complete process: locate the module, load it, execute it, and make its contents available.
Common mistake Ignoring naming conflicts.
What it looks likeCreating a file with the same name as a standard library module.
Why it failsThe search order can cause Python to find the local file instead of the intended module.
Do this insteadChoose module names carefully and consider the order of directories in sys.path.
Practice the Import Trace
A program requests a non-built-in module. The requested file is absent from the first directory in sys.path but present in the next directory. Later, the program imports the same module again after the module has changed some internal state. Trace what Python does during the first import and the later import.
Hints
- Start with the order of directories in
sys.path. - Ask when the module's code is executed.
- Ask whether the later import searches for and initializes a new copy or reuses the cached module.
- 1A strong trace should state that Python checks the first directory, continues because there is no match, and loads the first matching file in the next directory. It should then state that the module's code runs during this first import, the initialized module is cached, and the later import reuses that cached module without re-running its initialization code.
Key Takeaways
- 1An import statement starts a locate, load, execute, and availability process. For non-built-in modules, Python searches the directories in
sys.pathin order and stops at the first matching file. A module's initialization code runs only during its first import. Python caches the initialized module, so later imports reuse it and preserve its state. Understanding this process helps explain naming conflicts, import-related errors, and behavior that does not match a re-initialization assumption.
At a glance
- An import statement tells Python to locate a module, execute its code, and make its contents available.
- Python searches non-built-in modules through the ordered directories in
sys.path. - The first matching module file ends the search.
- Module initialization runs once, after which Python reuses the cached module.
- Search order and caching explain naming conflicts, persistent module state, and many import-related behaviors.