Import Modules in Python

Learn how the Python import statement works, where Python searches for modules, and how to avoid common import pitfalls like naming conflicts and circular dependencies.

6 min read

The import statement is the mechanism that connects your code to every other piece of Python code on your system, including the standard library, third-party packages, and your own modules and packages. To import modules in Python, you use this statement to bring external code into your current namespace. Now that you have learned about modules and packages from the earlier articles in this section, you are ready to understand exactly what happens when Python encounters an import and how to control that process.

When Python encounters an import statement, it performs a consistent sequence of steps. Understanding this sequence turns import errors from frustrating mysteries into solvable problems with clear causes. The same understanding also helps you design your own projects so that other people can import your code without configuration guesswork.

The three-step import process

The first thing Python does when it encounters an import is check the module cache, a dictionary called sys.modules that holds every module that has already been loaded during the current interpreter session. If the requested module name is already a key in that dictionary, Python returns the cached module object immediately and does nothing else. This is why importing the same module twice in the same program does not cause its code to execute twice, and it is why editing a .py file while a program is running does not change the behavior of imports that happened before the edit.

If the module is not in the cache, Python begins a search through a list of directory paths. This list is built at startup from several sources: the directory containing the script that launched the interpreter, directories from environment variables, and the standard library directories that are part of the Python installation. If you have read the overview of Python modules and packages, you already know that this search path is why modules in the same directory as your script are importable without any special configuration. Python iterates through these directories in order and looks for a file with the matching name. The search stops at the first match, and the file it finds becomes the module.

After locating the file, Python creates a new module object and stores it in the cache immediately, before running any of the module's code. This ordering matters: it is what lets two modules import each other without Python looping forever, because the second import finds the first module already in the cache even though that module has not finished executing yet. Python then executes the file's contents from top to bottom, and the function definitions, class definitions, and variable assignments run and populate the module object's namespace. Once execution completes, the module name is bound to that object in the importing code's namespace. From that point forward, every name defined in the module is accessible through dot notation with the module name as the prefix.

A simple import in practice

To see the import process in action, create a module called calculator.py with a few mathematical utility functions. The module contains nothing but function definitions: an add function, a subtract function, a multiply function, and a divide function that raises an error on division by zero. Each function takes two arguments, performs a calculation, and returns the result.

pythonpython
def add(a, b):
    return a + b
 
def multiply(a, b):
    return a * b
 
def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

Now create a script in the same directory that imports this module and uses its functions. Every call is prefixed with the module name, which tells anyone reading the file exactly where each function comes from. In a file that imports from five or six different modules, this clarity prevents confusion about which function is being called and where a bug might live.

pythonpython
import calculator
 
result = calculator.add(12, 8)
print(f"12 + 8 = {result}")
 
result = calculator.multiply(4, 7)
print(f"4 * 7 = {result}")

The module prefix is not extra typing to tolerate; it is documentation. The plain import style is the recommended default for most situations, and you should reach for the from-import style only when the prefix genuinely becomes too verbose to be readable. The article on different ways to import Python modules covers all four import styles and when each one is appropriate.

Where Python looks for your modules

The search path determines which directories Python scans during an import. You can inspect the current search path at any time by printing sys.path from the standard library. On a typical system, the output includes the directory containing the script you ran at the beginning, followed by standard library directories, and finally site-packages directories where third-party libraries are installed. That first entry is not always your terminal's current working directory: if you run python scripts/main.py from a parent folder, sys.path[0] is scripts, not the folder you were standing in when you ran the command. That first entry being the script's own directory is the reason you can create a file in the same folder as your main script and import it by name without any path configuration.

If you need to add a directory to the search path at runtime, you can append to sys.path directly. This is sometimes necessary for one-off scripts or development workflows, but it should not be the permanent solution for a project you share with others. The proper long-term approach is to structure your project as an installable package, which is covered in the article on building a multi-module Python project.

When imports go wrong

The most common import error occurs when Python cannot find the module anywhere on the search path. The fix is usually one of three things: the file is misspelled, the file is in a directory that Python is not searching, or you are running Python from a different working directory than you expect. Print the search path and the current working directory at the top of your script to diagnose this quickly.

A more subtle error happens when a local module shadows a standard library module. If you create a file called json.py in your project directory, any import of json in your project will find your file instead of Python's built-in json module. Your file almost certainly does not provide the same functions, so code that relies on the standard library version will fail with an AttributeError. The fix is to rename your file to something that does not conflict with any standard library module name.

Circular imports are another common pain point. If module A imports from module B and module B also imports from module A, both modules fail to initialize completely because each one needs the other to finish loading first. Python detects this and raises an ImportError with a message about a partially initialized module. The fix involves restructuring either to eliminate the two-way dependency or to move the shared code into a third module that both A and B import unidirectionally. The article on avoiding circular imports in Python covers this pattern in detail with refactoring examples.

Rune AI

Rune AI

Key Insights

  • The import statement tells Python to find, execute, and cache a module, making its names available in your program.
  • Python searches a list of directories in order, so a local file with the same name as a standard library module will shadow the built-in.
  • Once a module is imported, it is cached and will not be re-executed on subsequent imports of the same name.
  • Use import module_name as the default style because the module prefix makes it clear where each name comes from.
  • Never name your own modules after standard library modules or third-party packages you depend on.
RunePowered by Rune AI

Frequently Asked Questions

Why does Python sometimes say 'No module named X' even when the file exists?

Python can only find modules that are on the search path. If your module is in a directory that is not on the search path, Python will not find it even if the file exists on disk. The most common fix is to ensure the module is in the same directory as the script doing the import. Running Python from a different working directory than you expect is another frequent cause of this error.

Can I import a module whose name I only know at runtime?

Yes, using importlib.import_module from the standard library. This function accepts a string, so you can build the module name dynamically. However, dynamic imports make code harder to read and tools like linters cannot check them, so use this only when you genuinely need runtime module resolution and not as a substitute for a regular import statement.

What happens if two modules try to import each other?

This creates a circular import, which causes an ImportError because each module needs the other to finish loading before it can complete its own initialization. Python detects circular dependencies and raises an error. The fix is to restructure the code so the dependency goes in one direction, or to move the shared dependency into a third module that both can import safely.

Conclusion

The import statement is the gateway to every module in the Python ecosystem. Understanding how it resolves names, where it searches for files, and what happens after a module is loaded gives you the confidence to structure your own projects and to troubleshoot import errors when they appear. Start with simple imports, add the module-name prefix when you call functions, and introduce from-imports only when they genuinely improve readability.