__init__.py turns directories into packages. Here's what you need to know.
Basic Package Structure
mypackage/
├── __init__.py
├── module_a.py
└── module_b.py
With __init__.py, you can import:
import mypackage
from mypackage import module_a
from mypackage.module_b import somethingEmpty init.py
The simplest case:
# mypackage/__init__.py
# (empty file)This just marks the directory as a package. Users import submodules directly:
from mypackage.module_a import func_aExporting a Public API
Control what's available at the package level:
# mypackage/__init__.py
from mypackage.module_a import func_a, ClassA
from mypackage.module_b import func_b
__all__ = ["func_a", "ClassA", "func_b"]Now users can do:
from mypackage import func_a, ClassA
# Instead of:
from mypackage.module_a import func_aall
Defines what from package import * imports:
# mypackage/__init__.py
__all__ = ["public_func", "PublicClass"]
def public_func():
pass
def _private_func():
pass
class PublicClass:
passfrom mypackage import *
# Only imports public_func and PublicClassAlso helps IDE autocompletion and documentation.
Lazy Loading
For large packages, defer imports:
# mypackage/__init__.py
def __getattr__(name):
if name == "heavy_module":
from mypackage import heavy_module
return heavy_module
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")Module only loads when accessed.
Package Initialization Code
Run code when package is imported:
# mypackage/__init__.py
print("Package loaded!") # Runs on first import
# Setup logging
import logging
logging.getLogger(__name__).addHandler(logging.NullHandler())
# Version
__version__ = "1.0.0"Keep initialization minimal—heavy work slows imports.
Subpackages
mypackage/
├── __init__.py
├── sub1/
│ ├── __init__.py
│ └── module.py
└── sub2/
├── __init__.py
└── module.py
Each directory with __init__.py is a package:
from mypackage.sub1 import module
from mypackage.sub2.module import somethingRelative Imports
Inside packages, use relative imports:
# mypackage/module_b.py
from .module_a import func_a # Same package
from ..other_package import something # Parent package# . = current package
# .. = parent package
# ... = grandparent packageCommon Patterns
Re-export submodules
# mypackage/__init__.py
from mypackage import module_a
from mypackage import module_b
# Users can do: mypackage.module_a.func()Version info
# mypackage/__init__.py
__version__ = "1.0.0"
__author__ = "Owen"Flatten imports
# mypackage/__init__.py
from mypackage.core import Client, Config
from mypackage.utils import helper
# Users get: from mypackage import ClientWithout init.py
Since Python 3.3, namespace packages work without __init__.py:
mypackage/
├── module_a.py
└── module_b.py
Still imports work:
from mypackage import module_aBut: explicit __init__.py is still recommended for clarity.
My Rules
- Always create
__init__.py— explicit is better - Keep it minimal — avoid heavy initialization
- Export public API — hide implementation details
- Use
__all__— document public interface - Use relative imports — inside packages
A well-designed __init__.py makes packages pleasant to use.