TL;DR: It doesn't, it's just for convenience when the package is too large or when you want to allow a star-import for a user.
It extends the default allowed symbols ([]
for you, see __all__
value) for the star import (from x import *
) so it contains all of the pre-defined symbols from apps.__all__
and also apps
itself as well.
That means if your module is called hello
and I do this:
# apps.py
__all__ = [x, y, z]
# __init__.py
__all__ = []
from . import apps
from .apps import *
__all__.extend(apps.__all__)
__all__.append('apps')
# main.py
from hello import *
# imports __init__ and executes unguarded lines
# __all__ in __init__.py is initialized
# apps.py is imported
# apps.py code is executed
# __all__ for apps.py is assembled
# star-import from apps.py imports all allowed symbols from apps.py
# __all__ is extended with "apps" and apps.__all__
# then your star-import in main.py would expose all of the listed symbols
I'll have these symbols available as "variables" in the module I issue the import in:
x
y
z
apps
i.e. I can then reference in my main.py
: x
, y
, z
contained in the apps.py
module directly or I can reference them through the apps
value (main.apps
after star import) like this: apps.x
, apps.y
, apps.z
.
If you do not specify the __all__
you can import everything as the __all__
variable serves only as a guard and this way of using it in the __init__.py
of a package ensures you can star-import just by package's name and have all of the necessary functions, constants, etc present without knowing the package's structure. However it can be also problematic with circular imports.