Python gives you a number of options for exporting the public portions of your package’s API depending on the needs of your users. These are exposed via __init__.py. This allows the developer to have a module structure that is optimized for developers while exposing an API that is optimal for the end user.
Given the following example package with 3 modules:
/src
/example_pkg
__init__.py
foo.py
bar.py
baz.py
setup.py
README.md
LICENSE
And the functions from each module:
## foo.py
def foo_func():
print(‘this is a foo function’)
## bar.py
def bar_func():
print(‘this is a bar function’)
## baz.py
def baz_func():
print(‘this is a baz function’)
You can take a few different approaches, each of which have their own advantages and disadvantages, which are outlined in the source article. This serves as a quick references
The Naive Approach#
By leaving __init__.py empty, you expose the underlying module structure to the user. This will expose all functions in all modules in the package.
import example_pkg
example_pkg.foo.foo_func()
## or
from example_pkg import bar
bar.bar_func()
## or
import example_pkg.baz as ex_baz
ex_baz.baz_func()
You can achieve the same flexibility for limited modules by adding the modules you want to expose to __init__.py:
import example_pkg.foo
import example_pkg.bar
import example_pkg.baz
Make All Functions Public Under Package#
This enables your user to access all functions in the module(s) you make public but under the package’s namespace instead of the module’s within the package.
__init__.py
from .foo import *
from .bar import *
from .baz import *
Calling the functions:
import example_pkg
example_pkg.foo_func()
example_pkg.bar_func()
example_pkg.baz_func()
This has the added advantage of hiding module functions marked private with a single underscore due to the import *.
Make Specific Functions Public Under Package#
This enables you to explicitly define which functions are made public by your module. This method is the least flexible for the user, but for smaller packages allows the developer to create a clean interface for users. This doesn’t scale well for larger packages, however.
__init__.py
from .foo import foo_func
from .bar import bar_func
from .baz import baz_func
Calling the functions:
import example_pkg
example_pkg.foo_func()
example_pkg.bar_func()
example_pkg.baz_func()
Control import * Explicitly with __all__#
The wildcard approaches above rely on naming conventions (the single-underscore prefix) to decide what from .foo import * re-exports. __all__ makes that contract explicit instead of implicit: it’s a list of strings naming exactly the public symbols, and it overrides the underscore heuristic entirely.
In a module (foo.py), __all__ defines what import * pulls in:
## foo.py
__all__ = ["foo_func"]
def foo_func():
print("this is a foo function")
def _helper(): # excluded from `import *` regardless of __all__
...
In a package’s __init__.py, __all__ controls which names from example_pkg import * exposes — useful for curating the flattened API from the “Make All Functions Public” approach without leaking helpers:
## __init__.py
from .foo import *
from .bar import *
from .baz import *
__all__ = ["foo_func", "bar_func", "baz_func"]
Notes and caveats:
__all__only affectsimport *. Explicit imports (from example_pkg import _helper) still work — it’s a convention for the public surface, not an access control mechanism.- If
__all__is absent,import *falls back to importing every name not prefixed with an underscore. - Listing a name in
__all__that doesn’t exist raisesAttributeErrorat import time, so it doubles as a lightweight check that your public API names are spelled correctly. - For packages, names in
__all__can be submodules as well as functions/classes, in which caseimport *imports those submodules.