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.
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.
Start with sys.path: Python begins with the first directory listed in sys.path.
Check directory order: Python checks each directory in the order provided by sys.path.
Stop 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.
Recognize 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.
Initial import: Python locates the module, executes its module-body code, and creates the module's variables and functions.
Module becomes cached: After initialization, Python keeps the loaded module in memory.
State changes: The module's counter or other state can change while the program executes.
Second 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
Assuming Python searches every possible location at the same time.
Python checks directories listed in sys.path in a specific order and stops at the first match.
Fix:
Reason from the ordered entries in sys.path and identify which directory is checked first.Expecting a second import to run the module's initialization code again.
Python caches the module after the first import and reuses it for later imports.
Fix:
Expect later imports to reuse the initialized module and preserve its state during the program's execution.Assuming an import only retrieves something already present.
An import can trigger a search, file loading, code execution, and exposure of module contents.
Fix:
Trace the complete process: locate the module, load it, execute it, and make its contents available.Ignoring naming conflicts.
The search order can cause Python to find the local file instead of the intended module.
Fix:
Choose 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.
- A 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
- An import statement starts a locate, load, execute, and availability process. For non-built-in modules, Python searches the directories in sys.path in 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.
Key Takeaways
- 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.