To use a function from another file: from helpers import clean_text, where helpers.py sits next to the file you are running. That works until your project grows folders, and then you meet ModuleNotFoundError. This guide covers the simple case, the folder case, and the one rule that explains every import error you will hit.
The simple case#
Two files in the same folder:
# helpers.py
def clean_text(text):
return text.strip().lower()
def word_count(text):
return len(text.split())
# main.py
from helpers import clean_text, word_count
print(clean_text(" Hello World ")) # hello world
print(word_count("one two three")) # 3
Note there is no .py in the import. You import the module name, not the file name.
The four forms#
import helpers # the whole module
helpers.clean_text(" x ")
from helpers import clean_text # one name
clean_text(" x ")
from helpers import clean_text as clean # renamed
clean(" x ")
from helpers import * # everything - avoid
Avoid the last one. It hides where names came from, and it will silently overwrite something you already defined. The only common exception is an interactive session where you are experimenting.
Between the first two, import helpers is often the better choice in a large file: helpers.clean_text(...) at the call site tells the reader exactly where that function lives.
The rule behind every import error#
Python searches sys.path, in order. The first entry is the folder containing the script you ran — not the folder of the file doing the importing.
import sys
for entry in sys.path:
print(entry)
Almost every ModuleNotFoundError for your own code means the module’s folder is not on that list. Printing sys.path is the fastest way to see why.
Importing from a subfolder#
project/
main.py
utils/
__init__.py
text.py
# main.py
from utils.text import clean_text
Running python main.py from project/ puts project/ on the path, so utils is found. The __init__.py file can be empty; it marks the folder as a package and avoids a class of ambiguity.
Why running a file directly can fail#
project/
package/
__init__.py
helpers.py
main.py <- imports from helpers
cd project
python package/main.py
ModuleNotFoundError: No module named 'package'
Running the file directly puts project/package/ on the path, not project/. From inside that folder, there is no package to import.
python -m package.main
The -m flag runs the module while keeping the current directory on the path, so package resolves. This one difference explains most “it works in my IDE but not from the terminal” reports.
Relative imports#
Inside a package, you can import relative to the current module:
# package/main.py
from .helpers import clean_text # same folder
from ..config import SETTINGS # one level up
The catch: relative imports only work when the file is being run as part of a package. Run it directly and you get:
ImportError: attempted relative import with no known parent package
Which is the same problem as above with a different message. The fix is the same: python -m package.main.
Two mistakes worth naming#
Shadowing a standard library module#
# your file: random.py
import random
print(random.randint(1, 10))
AttributeError: module 'random' has no attribute 'randint'
Your own file was found first, so it imported itself. The same happens with csv.py, json.py, email.py, string.py and test.py. Rename the file, and delete the __pycache__ folder afterwards.
Circular imports#
# a.py
from b import helper
# b.py
from a import setting # ImportError
Each file needs the other before it has finished loading. Three ways out, best first: move the shared thing into a third module both can import; import inside the function rather than at the top; or import the module rather than the name, which delays the lookup to call time.
Importing from anywhere#
Occasionally you genuinely need a module from an unrelated folder:
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from shared.helpers import clean_text
This works but is a last resort. It makes the code depend on where the file happens to sit. The tidier answer for anything long-lived is to make the shared code an installable package:
python -m pip install -e .
With a minimal pyproject.toml, that puts your package on the path properly, and imports work from any directory.
What actually happens on import#
# helpers.py
print("helpers is loading")
def clean_text(text):
return text.strip()
import helpers
import helpers # nothing printed the second time
Python runs the module’s code once and caches it in sys.modules. That is why top-level code in a module runs at import time, and why anything that should only run when the file is the entry point belongs under a guard:
def main():
print("running")
if __name__ == "__main__":
main()
Questions people ask#
Do I need __init__.py?
Not strictly since Python 3.3, but including it is still the safer habit. It makes the package boundary explicit and avoids surprises when two folders share a name.
Should I use absolute or relative imports?
Absolute for almost everything — they say exactly where the code lives and survive files being moved. Relative imports are reasonable inside a self-contained library where the internal structure is stable.
Why does my import work in Jupyter but not in a script?
Jupyter starts with the notebook’s folder on the path, which is often not the same folder a script would use. Print sys.path in both and compare.
How do I reload a module after editing it?
In a script, just run it again. In an interactive session, importlib.reload(module), though restarting the kernel is more reliable.
Where to go next#
- Building a complete Python project — the folder structure this assumes.
- Python functions explained — writing the functions you are importing.
- Fixing ModuleNotFoundError — when the missing module is a library, not yours.