Skip to content

The Import System & Module Lifecycle

In Python, no file is an island. The Import System is the mechanism that allows you to break your code into manageable, reusable pieces. While import math seems simple, the underlying process involves complex search paths, bytecode compilation, and a sophisticated caching mechanism.


When you type import module_name, you aren’t just “linking” to a file. You are executing a statement that performs three distinct operations:

  1. Search: Finding the physical file on your disk.
  2. Compile: Converting the source code into Bytecode (.pyc files).
  3. Execute: Running the code inside the module to create its namespace.
SyntaxUse CaseResult
import mathStandardCreates math object; access via math.pi.
from math import piSpecificInjects pi directly into your namespace.
import math as mAliasingRenames math to m locally.
from math import *WildcardDanger! Injects all public names. Avoid in production.

2. Detailed Explanation: How Python Finds Modules

Section titled “2. Detailed Explanation: How Python Finds Modules”

Python doesn’t search your entire hard drive. It looks in a specific list of directories stored in sys.path.

When you trigger an import, Python checks these locations in order:

  1. The Home Directory: The folder containing the script you are currently running.
  2. PYTHONPATH: An environment variable containing additional directories.
  3. Standard Library: The built-in modules that come with Python (e.g., os, sys, math).
  4. Site-Packages: Where third-party libraries (installed via pip) are stored.
check_path.py
import sys
for path in sys.path:
print(path)

Python is efficient. It only ever loads a module once per program execution.

When a module is imported, the resulting module object is stored in a dictionary called sys.modules.

  • Subsequent imports of the same module simply look up the object in this dictionary.
  • This is why global code in a module (like a print statement) only runs once, no matter how many times you import it.

To speed up startup, Python saves the compiled version of your module in a folder called __pycache__. The .pyc files are platform-independent bytecode that the Python Virtual Machine can run directly without re-parsing the .py file.


4. Execution Context: __name__ == "__main__"

Section titled “4. Execution Context: __name__ == "__main__"”

Every module has a built-in attribute called __name__. Its value depends on how the file is being used.

  • If the file is run directly: __name__ is set to the string "__main__".
  • If the file is imported: __name__ is set to the module’s filename.
script.py
def main():
print("This only runs when the script is executed directly!")
if __name__ == "__main__":
main()

Context: This pattern allows you to write files that can be used both as standalone scripts and as importable modules without accidentally running code during the import.


As your projects grow into packages (folders of modules), you need to decide how to reference them.

Specifies the full path from the project’s root. Clear and preferred.

from my_project.utils.networking import send_request

Uses “dots” to refer to the current or parent package.

  • . (current package)
  • .. (parent package)
from .models import User
from ..config import SETTINGS

A circular import occurs when Module A imports Module B, but Module B also imports Module A.

You will often see an AttributeError or ImportError stating that a specific name cannot be found, even though you can see it in the file.

  1. Refactor: Move the shared logic into a third Module C that both A and B import.
  2. Deferred Import: Move the import statement inside a function so it only runs when needed, rather than at the top of the file.
deferred_fix.py
# Inside module_a.py
def do_something():
import module_b # Import only when function is called
module_b.logic()

If you ever need to import a module whose name you don’t know until the program is running (dynamic imports), Python provides the importlib library.

dynamic_import.py
import importlib
plugin_name = "custom_plugin" # This could come from a config file
plugin = importlib.import_module(plugin_name)
plugin.run()