导入模块:__main__ vs import as module
Posted
技术标签:
【中文标题】导入模块:__main__ vs import as module【英文标题】:Importing modules: __main__ vs import as module 【发布时间】:2012-10-22 07:19:19 【问题描述】:作为序言,我想我可能已经想出了如何让这段代码工作(基于Changing module variables after import),但我的问题实际上是关于为什么会发生以下行为,以便我可以理解将来不应该做什么。
我有三个文件。第一个是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
,是否可以访问mod1.py
的__main__
版本中的变量,而不是mod2.py
导入的版本中的变量?
【问题讨论】:
如果您重构以消除循环导入,您将帮自己一个忙。 【参考方案1】:关于使用模块作为主脚本的实用解决方案 - 支持一致的交叉导入:
解决方案 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)
失败
腌制的类需要额外的映射来解除腌制 (find_global
..)
【讨论】:
【参考方案2】:__name__
变量总是包含模块的名称,except 当文件作为脚本加载到解释器中时。 然后该变量被设置为字符串'__main__'
。
毕竟,脚本作为整个程序的主文件运行,其他一切都是由该主文件直接或间接导入的模块。通过测试__name__
变量,您可以检测文件是作为模块导入的,还是直接运行的。
在内部,模块被赋予一个命名空间字典,该字典作为每个模块元数据的一部分存储在sys.modules
中。主文件,即执行脚本,存储在与'__main__'
相同的结构中。
但是当您将文件作为模块导入时,python 首先会在sys.modules
中查看该模块之前是否已经导入过。所以,import mod1
意味着我们首先在sys.modules
中查找mod1
模块。如果mod1
不存在,它将创建一个带有命名空间的新模块结构。
所以,如果你们都将mod1.py
作为主文件运行,并且稍后将其作为 python 模块导入,它将在@987654332 中获得 两个 命名空间条目@。一个是'__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
会找到的。
【讨论】:
这样可以吗?if __name__ == '__main__': sys.modules['mod1'] = sys.modules['__main__']; func1A()
@Kos:我不会那样做,我想你会发现很多假设都会被打破。避免将模块用作脚本。
@Brendan:约定使用tests.py
模块或tests
包,使用unittests
作为框架。
@variable:因为调用了mod1.func1B()
,所以正在使用mod1
中的函数副本。函数在它们自己的模块对象中使用全局变量。注意还有一个__main__.func1B()
函数,它连接到__main__
全局变量。
啊,我想我明白了。这里的问题是该值是在 main 命名空间中设置的。但是 mod2 函数正在从 mod1 命名空间访问它以上是关于导入模块:__main__ vs import as module的主要内容,如果未能解决你的问题,请参考以下文章