I'm struggling to understand how __name__
of a module be defined, especially in different import methods. Suppose a directory tree:
├ main.py
└ test/
├── __init__.py
├── a
│ ├── a1.py
│ ├── base.py <- a1.py and b1.py will import it
│ └── __init__.py
└── b
├── b1.py
└── __init__.py
And here is base.py
:
# test/a/base.py
print("Base.py be imported, __name__ =", __name__)
a1.py
imports base.py
through relative import
# test/a/a1.py
print("[a1.py] import base: start")
from . import base
print("[a1.py] import base: end")
then a/__init__.py
imports a1.py
and base.py
# test/a/__init__.py
from . import base
from .a1 import *
For package b
, the module b1.py
import base through absolute import
# test/b/b1.py
print("[b1.py] import base: start")
from test.a import base
print("[b1.py] import base: end")
and b/__init__.py
import b.py
# test/b/__init__.py
from test.b.b1 import *
Finally, the test/__init__.py
will import both package a
and b
in different methods
# method1
print(" ----- import a -----")
from .a import *
print(" ----- import b -----")
from .b import *
# method2
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/./")
print(" ----- import a -----")
from a import *
print(" ----- import b -----")
from b import *
If I import test
in main.py
, the output looks like it in method1
----- import a -----
Base.py be imported!, __name__ = test.a.base
[a1.py] import base: start
[a1.py] import base: end
----- import b -----
[b1.py] import base: start
[b1.py] import base: end
The __name__
of base
is test.a.base
. If I try it again in method2, the output looks like
----- import a -----
Base.py be imported!, __name__ = a.base
[a1.py] import base: start
[a1.py] import base: end
----- import b -----
[b1.py] import base start
Base.py be imported!, __name__ = test.a.base
[a1.py] import base: start
[a1.py] import base: end
[b1.py] import base: end
which is quite different to the method1.
How a module's __name__
be defined? Does different import method, like absolute/relative import or different import seaching path, change the module's __name__
? I have seen this post but it still confuse me, and I want to know more about it under the hood. Thanks a lot!
The problem is with sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/./")
. Before you run this line, the path presumably contains the directory in which main.py
lives. The only top-level package on that path is test
. After, you have a
and b
available as well.
Python modules are named by path relative to package root. Before the path modification, you have the following packages available:
test
test.a
test.a.a1
test.a.base
test.b
test.b.b1
After the modification, you add the following additional modules:
a
a.a1
a.base
b
b.b1
As far as python is concerned, these are entirely distinct modules coming from different packages, since the full names are different. If you inspect sys.modules
before and after the modification, you will find that a new set of module objects with the new names has indeed been added.
Relative imports are resolved from the name of the module doing the import. So in a.py
, you import test.a.base
the first time and a.base
the second time. However, the fully qualified imports starting with test.
will always import from the first load.
The problem with this approach is that any modifications you make to say a.a1
will not propagate to test.a.a1
, which might be unexpected. That's one reason not to mess needlessly with the import path.
Two additional notes:
/./
in your modified path. That's totally unnecessary. It means "current directory", which is meaningless after a file name, although the file manipulation routines seem to be able to handle it in this case. Had you done /..
instead, there would have been no change because you would have added the parent directory of test
again.__main__
when you first run it. This can lead to similar unexpected behavior, which is why it's not recommended to make library modules executable. You seem to have done a good job with that in how you placed main.py
.