Python import operations in complex package structures?

Consider the following hierarchy of three regular packages and their contents:

quick ├── brown │ ├── fox.py │ └── __init__.py ├── lazy │ ├── dog.py │ └── __init__.py └── __init__.py 

Now suppose the dog module has a jump function and is needed in the fox module. How do I proceed?

Recently I saw Raymond Hettinger talking at Pycon 2015. I would like a function that should be directly imported from the root of the lazy package, for example:

 from lazy import jump 

In addition, it seems to me that the record of relative imports is shorter and simplifies the visibility of intra-packages. Therefore, I would write this in lazy/__init__.py :

 from .dog import jump 

And this is in fox.py :

 from ..lazy import jump 

But I wonder if this is right?

At first, importing the name jump into lazy/__init__.py does nothing to prevent the import from dog . Could this cause problems if a function is potentially imported from many places? For example, in unit testing, can we possibly decapitate a name from the wrong location?

Moreover, IDEs with their automatic import procedures seem to prefer to import from the module where the function is defined. I could override this by putting the _ character in front of all module names, but this seems a little impractical.

Otherwise, is it dangerous to cast all the names that are needed outside the package before __init__.py ? This is likely to at least increase the possibility of circular imports. But I think that if a circular import occurs, there is something fundamentally wrong with the package structure anyway.

What about relative imports? PEP 8 says that absolute imports are recommended: what does this mean when it is said that absolute imports behave better than relative ones? Can you give me an example?

+5
source share
1 answer

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

+2
source

All Articles