Import modules by organizing your project into packages and using relative or absolute imports

A large Python project needs a clear folder structure so you can import code from one file into another without confusion or errors. The simplest approach is to create a package — a folder with an __init__.py file inside it — and then import from that package using either absolute imports (naming the full path from your project root) or relative imports (naming the path relative to the current file). Most projects use absolute imports because they are easier to read and less fragile when files move around.

When your project grows beyond a few files, Python needs to know where to find the modules you are trying to import. Without a clear structure, you end up with import errors or code that only works when you run it from a specific folder. This guide shows you how to set up your project so imports work reliably, no matter where you run your code from.

Key Takeaways

  • Create a folder structure with __init__.py files to turn folders into packages that Python can import from.
  • Use absolute imports (naming the full path from your project root) for most code because they are clearer and more portable than relative imports.
  • Add your project root to Python's module search path using sys.path or by running code from the correct directory.
  • Keep your main entry point (the file you run directly) separate from the modules it imports, so imports always point downward into your package structure.
  • Use from package.module import function instead of import package.module when you only need one piece of code, to keep your code readable.

Set up your project with packages and an __init__.py file

A package is a folder that contains an __init__.py file. Python treats any folder with this file as a package, which means you can import code from the modules (Python files) inside it. The __init__.py file can be empty, or it can contain setup code that runs when the package is imported.

Create a folder structure like this for a medium-sized project:

my_project/ ├── main.py ├── myapp/ │ ├── __init__.py │ ├── database.py │ ├── utils.py │ └── api/ │ ├── __init__.py │ ├── routes.py │ └── handlers.py └── tests/ ├── __init__.py └── test_database.py

The __init__.py files tell Python that myapp and myapp/api are packages. Without them, Python treats the folders as plain directories and cannot import from them. You can leave these files empty when you are starting out.

Use absolute imports to reference code from your project root

An absolute import names the full path to a module starting from your project root. If you are in myapp/api/routes.py and you want to use a function from myapp/database.py, you write:

from myapp.database import get_user

This import works the same way no matter which file you are in or where you run your code from (as long as your project root is in Python's search path). Absolute imports are the standard in large projects because they are explicit — anyone reading the code can see exactly where the function comes from.

If you need everything from a module, you can write from myapp.database import *, but this is usually a bad idea in large projects because it makes it hard to see where a function came from. Instead, import only what you need: from myapp.database import get_user, save_user.

Add your project root to Python's module search path

Python looks for modules in a list of directories called sys.path. When you run a script, Python automatically adds the directory that script is in to sys.path, but it does not always add your project root. If you run python main.py from inside your project folder, Python adds that folder to the search path, and absolute imports work. If you run Python from a different directory, the imports may fail.

The safest approach is to always run your main script from your project root. If you use a tool like pytest for testing or flask for web development, these tools handle the path for you automatically. If you are running a custom script, add this code at the very top of your entry point (the file you run directly):

import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent))

This adds your project root to the search path before any imports happen, so absolute imports will work.

Use relative imports only for code within the same package

A relative import references a module relative to the current file, using dots. If you are in myapp/api/routes.py and you want to import from myapp/api/handlers.py (the same package), you can write:

from .handlers import process_request

The single dot means "the current package". Two dots (..) mean "the parent package". Relative imports are useful when you are moving code around within a package and want the imports to stay valid, but they are harder to read and can break if you reorganize your folder structure.

In large projects, most developers stick with absolute imports everywhere because they are clearer. Use relative imports only when you are importing from a sibling module in the same package, and even then, absolute imports are usually the better choice.

Organize your code so imports point downward

In a well-organized project, imports should flow in one direction: from the top of your folder structure downward. Your main entry point (main.py) imports from myapp, which imports from myapp/api, but myapp/api should never import from myapp at the top level. This prevents circular imports, where module A imports module B, and module B imports module A, causing both to fail.

If you find yourself needing to import upward, it usually means you should move the shared code into a new module that both can import from. For example, if myapp/api/routes.py needs a function that myapp/database.py also uses, move that function to myapp/shared.py and have both import from there.

Keep your entry point straightforward. The main.py file should mostly import from your packages and call functions, not contain the actual logic. This makes it clear what your project does and keeps the imports organized.

Handle imports in __init__.py to simplify code elsewhere

The __init__.py file in a package can import code from the modules inside it and re-export it. This lets other files import from the package directly instead of from specific modules. For example, in myapp/__init__.py, you could write:

from myapp.database import get_user, save_user from myapp.utils import format_date

Now other files can write from myapp import get_user instead of from myapp.database import get_user. This is useful when you want to hide the internal structure of your package and make the public interface clear.

Do not overuse this pattern. If your __init__.py imports everything from every module, it becomes a dumping ground and defeats the purpose of organizing your code. Use it only for the functions and classes that other parts of your project actually need.

Frequently Asked Questions

What is the difference between import myapp and from myapp import something?

import myapp loads the entire package and requires you to use the full name: myapp.get_user(). from myapp import get_user loads only that function and lets you use it directly: get_user(). The second form is usually clearer in large projects because it shows exactly what you are using.

Why do I get a ModuleNotFoundError even though the file exists?

Python cannot find the module because either the folder is not a package (missing __init__.py), the module is not in Python's search path, or you are using the wrong import path. Check that every folder in your import path has an __init__.py file, and make sure you are running your code from your project root or have added it to sys.path.

Can I use relative imports in my main entry point?

No. Relative imports only work inside packages, not in the main script you run directly. Your entry point should always use absolute imports. This is another reason to keep your main script straightforward and separate from your package code.

What happens if two modules try to import each other?

This creates a circular import, which usually causes an error or makes one of the imports incomplete. Avoid it by moving shared code into a third module that both can import from, or by importing inside a function instead of at the top of the file (though this is a workaround, not a solution).