Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild
Back to Blog Explainer

Python __init__.py: What It Actually Does, and When Empty Is Correct

Sean

Platform Writer

Jul 21, 2026
8 min read

An init.py file marks the directory it sits in as a Python package, and it runs the first time anything imports that package. That is the whole job. It can be completely empty and still do that job perfectly. The confusion starts when people treat it as a place to dump code, wire up imports, or run side effects, and then wonder why a circular import blew up on startup. The right default for init.py is empty, and the second-best is a short, deliberate list of what the package exports.

Python __init__.py: What It Actually Does, and When Empty Is Correct

Since Python 3.3, packages can technically exist without it (namespace packages), which makes the question sharper: if it is optional, what is it for, and when do you actually need one?

Table of contents

What the file marks and when it runs

A directory with an __init__.py is a regular package. When you import mypackage, Python executes mypackage/__init__.py top to bottom, once, and caches the resulting module object in sys.modules. Import it again and you get the cached object, not a re-run.

myapp/
    __init__.py        # makes 'myapp' a package
    db.py
    routes/
        __init__.py    # makes 'myapp.routes' a subpackage
        users.py

That timing matters. Anything at the top level of __init__.py runs before the code that triggered the import gets to continue. Put a slow database connection there and every import myapp pays for it, including your test suite and your CLI --help.

The case for keeping it empty

An empty __init__.py is not a placeholder you forgot to fill in. It is a decision. It says the package is a namespace and nothing more, and it keeps import order boring, which is exactly what you want import order to be.

The failure mode people hit is the eager re-export: from .db import connection at the top of __init__.py, so callers can write from myapp import connection. It reads nicely until db.py imports something that imports myapp, and now you have a circular import that only appears when the modules load in a particular order. Empty init files never do this.

What legitimately belongs in it

Three things earn their place:

  • A curated public API. Re-export the handful of names you want callers to use, so from myapp import Client works instead of from myapp.client.base import Client. This is the strongest reason to write anything at all.
  • __all__. A list of names that from myapp import * will pull in. It also signals intent to readers and linters about what is public.
  • Package-level constants or a version string. __version__ = "1.4.0" is a common and harmless resident.
# myapp/__init__.py
from .client import Client
from .errors import ApiError

__version__ = "1.4.0"
__all__ = ["Client", "ApiError"]

Notice what is not here: no I/O, no network calls, no logging config, no reading environment variables. Those are side effects, and side effects at import time are how you end up with a package that cannot be imported inside a Docker build because it wants a database that is not running yet.

Namespace packages: the no-init exception

Since PEP 420, a directory with no __init__.py can still be imported as a namespace package. The main reason to use one is splitting a single logical package across multiple directories or distributions, for example a plugin system where mycompany.plugins.foo and mycompany.plugins.bar ship as separate installs.

For a normal application or library, do not reach for namespace packages. The absence of __init__.py also disables some tooling assumptions, and a missing init file is far more often a mistake (a folder that was never meant to be a package) than a deliberate namespace. Keep the explicit __init__.py unless you have a specific reason not to.

Imports inside a package: relative vs absolute

Inside routes/users.py, you reference a sibling module. Two ways:

from myapp.db import connection   # absolute - explicit, refactor-friendly
from ..db import connection        # relative - shorter, breaks if you move the file

Absolute imports are the safer default; they read the same no matter where the file is executed from. Relative imports are fine within a tightly-coupled package but fail loudly if you ever run the module as a script (python users.py), because there is no parent package in that context. If you keep hitting ImportError: attempted relative import with no known parent package, that is the cause.

How this fits the rest of the stack

A package that does I/O at import time is the same class of problem as infrastructure that does surprising work you did not budget for: the cost is hidden until something loads it in the wrong context. When you deploy a Python service, the platform imports your package to start it, so a heavy __init__.py becomes slower cold starts. The RunxBuild hosting calculator shows the compute, database, and storage line items for that service as separate numbers, so the sizing is something you checked rather than guessed, and the RunxBuild dashboard is where the deploy logs show exactly what ran at startup.

Useful related references:

FAQ

Do I still need init.py in Python 3?

Not strictly. Since Python 3.3, a directory without one can be imported as a namespace package. But for normal applications and libraries you should still add it: it declares the folder is a package on purpose, keeps tooling happy, and gives you a place for a curated public API. Treat namespace packages as a specific tool for splitting a package across distributions, not the default.

What should I put in init.py?

Ideally nothing, or a short set of re-exports that define the package’s public API, an all list, and maybe a version string. Avoid side effects: no database connections, no network calls, no reading environment variables, no logging setup. Anything at the top level runs on every import, including in tests and build steps where the resources it needs may not exist.

Can init.py be empty?

Yes, and empty is the correct default. An empty file still marks the directory as a package and still runs (doing nothing) on first import. It keeps import order simple and avoids circular-import surprises. Only add code when you have a concrete reason, such as exposing a cleaner import path for callers.

What is the difference between a module and a package?

A module is a single .py file. A package is a directory containing modules (and usually an init.py) that groups them under one namespace. Importing a package runs its init.py; importing a module runs that file. Packages let large projects organize code into subpackages like myapp.db and myapp.routes.

Why am I getting attempted relative import with no known parent package?

You ran a file that uses relative imports directly as a script, so Python has no parent package to resolve the dots against. Either run it as a module with python -m myapp.routes.users from the project root, or switch the relative import to an absolute one. Absolute imports avoid this class of error entirely.

#python __init__.py#python#packages#imports#dev-infra