Imports behave differently when in __init__.py that is imported

杀马特。学长 韩版系。学妹 提交于 2019-12-03 00:48:53

The situation is the following: you have a script (run.py), a package test and its submodule test.b.

Whenever you import a submodule in Python, the name of that submodule is automatically stored into the parent package. So that when you do import collections.abc (or from collections.abc import Iterable, or similar), the package collections automatically gets the attribute abc.

This is what's happening here. When you do:

from b import B

the name b is automatically loaded into test, because b is a submodule of the test package.

Even if you don't do import b explicitly, whenever you import that module, the name is placed into test. Because b is a submodule of test, and it belongs to test.


Side node: your code won't work with Python 3, because relative imports have been removed. To make your code work with Python 3, you would have to write:

from test.b import B

This syntax is perfectly identical to from b import B, but is much more explicit, and should help you understand what's going on.


Reference: the Python reference documentation also explains this behavior, and includes a helpful example, very similar to this situation (the difference is just that an absolute import is used, instead of a relative import).

When a submodule is loaded using any mechanism (e.g. importlib APIs, the import or import-from statements, or built-in __import__()) a binding is placed in the parent module's namespace to the submodule object. For example, if package spam has a submodule foo, after importing spam.foo, spam will have an attribute foo which is bound to the submodule.

Let's say you have the following directory structure:

spam/
    __init__.py
    foo.py
    bar.py

and spam/__init__.py has the following lines in it:

from .foo import Foo
from .bar import Bar

then executing the following puts a name binding to foo and bar in the spam module:

>>> import spam
>>> spam.foo
<module 'spam.foo' from '/tmp/imports/spam/foo.py'>
>>> spam.bar
<module 'spam.bar' from '/tmp/imports/spam/bar.py'>

Given Python's familiar name binding rules this might seem surprising, but it's actually a fundamental feature of the import system. The invariant holding is that if you have sys.modules['spam'] and sys.modules['spam.foo'] (as you would after the above import), the latter must appear as the foo attribute of the former.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!