抱歉,标题令人困惑。至少我试过了!这是我的目录结构:
root\
mypackage\
__init__.py
mymodule.py
main.py
我的模块.py
print('inside mymodule.py')
__init__.py
print('inside __init__')
from . import mymodule as m
主.py
import mypackage
print(mypackage.m)
print(mypackage.mymodule) # <--- Why does it work?
和输出:
inside __init__
inside mymodule.py
<module 'mypackage.mymodule' from 'C:\\Users...\\mypackage\\mymodule.py'>
<module 'mypackage.mymodule' from 'C:\\Users...\\mypackage\\mymodule.py'>
在main.py
文件中,当我导入mypackage
时,这个标签实际上是指__init__.py
文件,所以我可以访问该模块内的所有对象/标签。 mypackage.m
起作用是有道理的,因为 m
现在是 __init__.py
全局命名空间中的一个符号。
但是 __init__.py
的命名空间中没有 mymodule
键/符号,因为我将 mymodule
符号重新绑定(bind)到 m
标签通过 as m
。
问题那么,为什么这个print(mypackage.mymodule)
可以正常工作而不抛出任何异常?
附加信息:如果我在包中有另一个模块,比如说 temp.py
,那么 print(mypackage.temp)
将无法工作,因为 mypackage
引用__init__.py
.
另外对我来说有趣的是,如果我在 __init__.py
中编写 print(mymodule)
并运行 main.py
模块,它将正确运行。
最佳答案
简答
在玩了两天导入语句并搜索文档后,我找到了答案。首先,我要说明发生这种情况的原因,然后再进行更详细的解释。 (另请参阅底部的更新部分)
看看这个link从文档中,还要检查该链接示例中提到的结构树:
If
__all__
is not defined, the statementfrom sound.effects import *
does not import all submodules from the packagesound.effects
into the current namespace; it only ensures that the packagesound.effects
has been imported (possibly running any initialization code in__init__.py
) and then imports whatever names are defined in the package. This includes any names defined (and submodules explicitly loaded) by__init__.py
.
我们如何在main.py
中导入mypackage
包并不重要,要么是import mypackage
,要么是from mypackage import *
形式无关紧要。
通过这样做,Python 导入了 __init__.py
模块中定义的所有名称,如我们在上面看到的 m
,还有子模块显式加载(此处为 mymodule
模块)。正确地说,它将 'mymodule'
键添加到 __init__.py
的全局命名空间。
让我们更详细地了解一下:
我要稍微更改一下 __init__.py
以便我们可以将它直接作为 main 模块运行(因为我们在内部使用了相对导入,所以我们不能这样做)然后我将打印它的全局命名空间中的内容。 (不要忘记用 PYTHONPATH 添加 mypackage
目录)
# __init__.py
print('inside __init__')
from mypackage import mymodule as m
print("--------------------------------")
for k, v in globals().copy().items():
if not k.startswith('__'):
print(k)
输出:
inside __init__
inside __init__
inside mymodule.py
--------------------------------
mymodule
m
--------------------------------
m
你看到 "inside __init__"
打印语句两次,因为这个文件被执行了两次,一次是自己执行,然后是通过执行这一行:from mypackage import mymodule as m
很明显,在虚线下,我们得到了不同的输出。第一个 mymodule
和 m
,第二个只有 m
。
当我们直接运行__init__.py
时,会在sys.modules
中添加一条名为'__main__'
的记录。但是当我们导入 mypackage
时,另一条记录被添加到名为 mypackage
的 sys.modules
中。有趣的是,它们都指向同一位置的同一文件但是从这些文件创建的模块对象并不相同。
为了演示这一点,我将在 mymodule.py
中添加几行代码。这有助于我们查看这些文件和模块:
# mymodule.py
print('inside mymodule.py')
import sys
v1 = sys.modules['mypackage']
v2 = sys.modules['__main__']
print(v1)
print(v2)
print(f'v1 is v2: {v1 is v2}')
print(f'v1 == v2: {v1 == v2}')
print(f'v1.__dict__ == v2.__dict__: {v1.__dict__ == v2.__dict__}')
print('mypackage', list(v1.__dict__))
print('__main__', list(v2.__dict__))
现在让我们再次直接运行__init__.py
模块!
inside __init__
inside __init__
inside mymodule.py
<module 'mypackage' from 'C:\\Users...\\mypackage\\__init__.py'> # these are the same
<module '__main__' from 'C:\\Users...\\mypackage\\__init__.py'> # these are the same
v1 is v2: False
v1 == v2: False
v1.__dict__ == v2.__dict__: False
mypackage ['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__']
__main__ ['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__']
--------------------------------
mymodule
m
--------------------------------
m
如输出所示,它们完全不同。 v1
是包,因为它有 __path__
。
Python 将 'mymodule'
键添加到 __init__.py
的命名空间的唯一情况是我们导入 import mypackage
并且它加载由 __init__.py
显式加载的所有子模块。
更新:
我从文档中找到了一个页面,它恰好解决了这个问题:
https://docs.python.org/3/reference/import.html#submodules
When a submodule is loaded using any mechanism, a binding is placed in the parent module’s namespace to the submodule object.
关于python - 从一个包中访问一个模块,该包的初始文件通过 AS 关键字导入该模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70152714/