Imports behave differently when in __init__.py that is imported

后端 未结 1 717
长发绾君心
长发绾君心 2021-02-05 19:53

Imports in an __init__.py seem to behave differently when the file is run, to when it is imported.

If we have the following files:

run.py

1条回答
  •  再見小時候
    2021-02-05 20:15

    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
    
    >>> spam.bar
    
    

    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.

    0 讨论(0)
提交回复
热议问题