funky-future
funky-future

Reputation: 3968

Why is a Python import implicitly executing parent modules?

As the Python documentation states, a module import (as well as imports from a module) is always executing possible parent modules. For example with these files that constitute a regular package:

a_package/
  __init__.py
  a_submodule.py

An import from a_submodule in a completely different package, e.g.

from a_package.a_submodule import an_object

would first execute a_package/__init__.py before executing a_package/a_submodule.py and eventually binding an_object into the namespace where the import is stated.

I'm wondering what the design rationale or technical necessity behind this is. In particular because it seems counter-intuitive to me as the usual package regime is an ordered graph where the parent modules are depending on contents from their subpackages, not vice versa. And as a matter of fact, if that import statement example was evaluated in a_package/__init__.py, it wouldn't trigger an execution of itself. Of course, because that'd be a cyclic dependency, yet it shows that submodules are not depending on their parent modules.

Upvotes: 1

Views: 324

Answers (1)

Davis Herring
Davis Herring

Reputation: 39838

There are several reasons. The most important from a capability perspective is that a package can use mechanisms like __path__ to help find its submodules, and that obviously won’t work if it weren’t configured before proceeding with the submodule import. Another is a convenience: if a package needs initialization before its modules can be used, it can be put into __init__.py rather than having every module need from . import startup.

Moreover, at what other time should the package be initialized? It doesn’t make sense to wait until all submodules are imported, since that might never happen; while it could be “after the first import of a submodule finishes”, that would still be before the import of other submodules and would be inconsistent and arbitrary. In particular, no module could use the contents of its parent __init__.py in case it was the first among its siblings to be imported.

As for the cyclic dependency, Python has always supported those (at least for absolute imports): importing a module already being imported simply produces a reference to the module object (in its partially formed state). The behavior of from . import foo (which is, as an aside, usually a bad idea as part of executing __init__.py) is no exception; as the “extra import” is just into sys.modules anyway, it has no effect at all.

Upvotes: 1

Related Questions