导入模块:__main__ 与 import as module
- 2025-03-26 09:10:00
- admin 原创
- 11
问题描述:
首先,我想我可能已经想出了如何让这段代码工作(基于导入后更改模块变量),但我的问题实际上是关于为什么会出现以下行为,这样我才能理解将来不该做什么。
我有三个文件。第一个是 mod1.py:
# mod1.py
import mod2
var1A = None
def func1A():
global var1
var1 = 'A'
mod2.func2()
def func1B():
global var1
print var1
if __name__ == '__main__':
func1A()
接下来我有 mod2.py:
# mod2.py
import mod1
def func2():
mod1.func1B()
最后我有了driver.py:
# driver.py
import mod1
if __name__ == '__main__':
mod1.func1A()
如果我执行命令python mod1.py
,则输出为None
。根据我上面引用的链接,似乎mod1.py
导入为__main__
和mod1.py
从导入之间存在一些区别mod2.py
。因此,我创建了driver.py
。如果我执行命令python driver.py
,则我得到预期的输出:A
。我似乎看到了差异,但我真的看不到机制或原因。这是怎么发生的以及为什么会发生?同一个模块会存在两次似乎有悖常理。如果我执行python mod1.py
,是否可以访问__main__
版本中的变量mod1.py
而不是导入的版本中的变量mod2.py
?
解决方案 1:
该__name__
变量始终包含模块的名称,除非文件已作为脚本加载到解释器中。然后该变量将设置为字符串'__main__'
。
毕竟,脚本随后作为整个程序的主文件运行,其他所有内容都是由该主文件直接或间接导入的模块。通过测试__name__
变量,您可以检测文件是否已作为模块导入,或是否直接运行。
在内部,模块被赋予一个命名空间字典,它作为每个模块元数据的一部分存储在 中sys.modules
。主文件(执行的脚本)存储在与 相同的结构中'__main__'
。
但是当你将文件作为模块导入时,python 首先会查看sys.modules
该模块是否之前已被导入。所以,import mod1
这意味着我们首先查找sys.modules
模块。如果尚未存在,mod1
它将创建一个带有命名空间的新模块结构。mod1
因此,如果您既将其mod1.py
作为主文件运行,又将其作为 python 模块导入,它将在 中获得两个命名空间条目sys.modules
。一个是'__main__'
,另一个是'mod1'
。这两个命名空间是完全独立的。您的全局变量var1
存储在 中sys.modules['__main__']
,但func1B
正在 中查找sys.modules['mod1']
,var1
其中 是None
。
但是当您使用 时python driver.py
,driver.py
它将成为'__main__'
程序的主文件,并且mod1
只会被导入到sys.modules['mod1']
结构中一次。这一次,func1A
存储var1
在sys.modules['mod1']
结构中,这就是func1B
要查找的内容。
解决方案 2:
关于使用模块作为主脚本的实用解决方案 - 支持一致的交叉导入:
解决方案 1:
例如参见 Python 的 pdb 模块,它是如何通过在执行时__main__
(在最后)导入自身作为脚本运行的:
#! /usr/bin/env python
"""A Python debugger."""
# (See pdb.doc for documentation.)
import sys
import linecache
...
# When invoked as main program, invoke the debugger on a script
if __name__ == '__main__':
import pdb
pdb.main()
我只是建议将__main__
启动重新组织到脚本的开头,如下所示:
#! /usr/bin/env python
"""A Python debugger."""
# When invoked as main program, invoke the debugger on a script
import sys
if __name__ == '__main__':
##assert os.path.splitext(os.path.basename(__file__))[0] == 'pdb'
import pdb
pdb.main()
sys.exit(0)
import linecache
...
这样,模块主体就不会被执行两次——这是“昂贵的”、不受欢迎的,有时甚至是至关重要的。
解决方案 2:
在极少数情况下,甚至希望将实际的脚本模块__main__
直接公开为实际的模块别名(mod1
):
# mod1.py
import mod2
...
if __name__ == '__main__':
# use main script directly as cross-importable module
_mod = sys.modules['mod1'] = sys.modules[__name__]
##_modname = os.path.splitext(os.path.basename(os.path.realpath(__file__)))[0]
##_mod = sys.modules[_modname] = sys.modules[__name__]
func1A()
已知缺点:
reload(_mod)
失败pickle 后的类需要额外的映射才能进行 unpickling (
find_global
..)