I am trying to create a local API for my team. I think I understand broadly the mechanics of _init_.py. Let's say we have the below package structure:
API/
├── __init__.py # Top-level package init file
└── core/
├── __init__.py # Core module init file
├── calculator.py
└── exceptions.py
Now if I build my API with empty _init_.py files, and then import API in my script, I won't be able to do something like:
import API
API.core
API.core.calculator
Because the submodules have not been imported specifically. What I need to do is to add into the following into my top-level _init_.py:
from . import core
And the following into my core module _init_.py:
from . import calculator
from . import exceptions
Now, when I do this, all imports I am making in my calculator.py or exceptions.py such as numpy or pandas are actually available through my package as follows:
API.core.calculator.numpy
On the same theme, let's say I want to access my calculator.py functions directly through the core keyword (let's assume _all_ variables are safely set up). Then I can add the following into my core module _init_.py
from .calculator import *
from .exceptions import *
which then allows me to do:
API.core.my_function()
But then again, I can also call API.core.calculator.my_function() at the same time, which might be confusing for users.
I tried mixing up the approaches, but with no results, please help !
The typical best practice is to define an __all__
list in each sub-module with names you want the sub-module to export so that the parent module can import just those names with a star import.
Names that you don't want exposed should be named with a leading underscore by convention so that the linters will warn the users if they try to import a "private" name.
So in your example case, your API/core/calculator.py
will look like:
import numpy as _numpy
__all__ = ['my_function']
def my_function(): ...
And then API/core/__init__.py
will star-import what's exported by calculator
(that is, just my_function
):
from .calculator import *
which then allows you to do:
API.core.my_function()
but not:
API.core._numpy
API.core.calculator._numpy
Note that the user can always do import API.core.calculator
explicitly to access _numpy
in that sub-module, but that is just how the module namespace works as there is nothing really private in Python.
Also note that a star import will not import names with leading underscores so if you do follow the convention of naming your "private" variables as such you don't even need to define an __all__
list.
Excerpt from the documentation of More on Modules:
There is even a variant to import all names that a module defines:
>>> from fibo import * fib(500) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
This imports all names except those beginning with an underscore (
_
).