Explicit interface declaration . If you want to expose the jump function as belonging to the lazy package, then it makes sense to include its lazy.__init__ , as you suggest. This way you make it clear that this is part of the lazy public interface. You also assume that other modules are not part of the public interface.
About how people / tools can import it directly from dog : in Python, privacy depends on the consent of the user, you cannot hide anything, but there are agreements.
Using underscores and the definition of dog._jump() makes it clear that dog does not want to expose _jump . And we could assume that any IDE tools should abide by this type of agreement. Anyway, if dog defines _jump and lazy provides jump , then you will not have a problem not knowing what is imported, because the names are different, so this is explicit, which is considered good in Python.
Here is a good pointer to this topic: Defining private module functions in python
About relative imports:: they are discouraged by PEP 8, however they were implemented for some reason, and they replaced implicit relative imports. The reason is PEP 8: especially when dealing with complex package mockups where using absolute imports will be overly detailed.
Final thoughts : in a word, if you consider the lazy package a library and do not want to expand the internal modules, then I think it makes sense to expose objects in lazy.__init__ . If, on the contrary, you want people to know that there is a dog module, then, in any case, allow other modules:
from lazy.dog import jump
or
from ..lazy import jump
If brown and brown.fox always packaged and tightly integrated with lazy , then I do not see the difference between absolute and relative, but I would rather prefer relative to explicitly indicate that you are referring to the internal module.
But if you think that they can be separated in the future, then relative imports do not make sense, and you prefer to do this depending on the above points:
from lazy.dog import jump
or:
from lazy import jump