Understanding `__init__.py` in Python
The __init__.py file is commonly used to define a regular Python package and control what happens when that package is imported. It can be empty, expose a package-level API, define metadata, or run lightweight initialization code.
1. A Basic Package
Consider this structure:
my_package/
├── __init__.py
├── module1.py
└── module2.pyWith __init__.py present, my_package is a regular package and its modules can be imported normally:
from my_package import module1
from my_package.module2 import some_functionSince Python 3.3, namespace packages can exist without __init__.py, so the file is no longer required for every possible package layout. It is still the standard choice for ordinary application and library packages.
2. Re-export a Public API
Suppose module1.py and module2.py contain useful functions. You can expose them at the package level:
# my_package/__init__.py
from .module1 import some_function
from .module2 import another_functionCallers can then write:
from my_package import some_function, another_functionThis can provide a simpler and more stable public API even if the internal module structure changes later.
3. What __all__ Does
You may define:
from .module1 import some_function
from .module2 import another_function
__all__ = ["some_function", "another_function"]__all__ primarily controls which names are imported by:
from my_package import *It can also communicate the intended public API to readers and tooling, but it does not make other attributes truly private.
4. Package Metadata
Small constants can live in __init__.py:
__version__ = "1.0.0"For installed package versions, modern projects may instead read metadata through importlib.metadata so the version has a single source of truth.
5. Avoid Heavy Import-Time Work
Code in __init__.py runs when the package is imported. Avoid slow network requests, large database connections, expensive filesystem scans, or other surprising side effects there.
Prefer lightweight imports and definitions:
from .client import Client
__all__ = ["Client"]6. Relative Imports
Inside a package, a leading dot refers to the current package:
from .module1 import some_functionTwo dots refer to the parent package:
from ..shared import helperUse relative imports deliberately; absolute imports are often easier to understand across larger codebases.
Conclusion
__init__.py remains an important part of conventional Python packages. Keep it lightweight, use it to expose a clear package-level API when helpful, and remember that namespace packages are a separate mechanism that can work without the file.