How to setup imports and dependencies between modules in the same python package

Maybe I should import the main functions into the init.py, like this [...] Is that best practice?

I wouldn't say there is a "best practice", it depends on the specific case, but this is surely pretty common: you define a bunch of stuff in each module, and import the relevant ones in __init__. This is an easy way to not bother the users with remembering which submodule has the needed function, however it can get pretty annoying if you have a lot of functions to import from each module.

What about the alternative to put ALL symbols into the init.py, such as

from .utils import *
from .fileio import *
from .math import *

You most likely don't want to do this. This will import everything in user scripts, including other imported modules and internal functions. You should avoid it.

What if the fileio.py needs to call some functions in the utils.py? [...] won't that create a circular or redundant reference?

Yeah, that is something that can happen and you usually want to avoid it at all costs. If you need some functions from utils.py in fileio.py, you should import them explicitly as from .utils import x, y, z. Remember to also always use relative imports when importing things between modules of the same package (i.e. use from .utils import x, not from package.utils import x).


A good compromise between these two options you mention which solves most of the above problems (although not circular imports, you would have to avoid those yourself) would be to define an __all__ list inside each one of your modules to specify which functions should be exported when using from x import *, like this:

# utils.py

import sys

__all__ = ['user_api_one', 'user_api_two']

def user_api_one():
    ...

def user_api_two():
    ...

def internal_function():
    ...

If you properly define an __all__ list in all your modules, then in your __init__.py you will be able to safely do:

from .utils import *
from .fileio import *
from .math import *

This will only import relevant functions (for example user_api_one and user_api_two for utils, and not internal_function nor sys).