Python 导入模块:主模块与导入为模块
在前言中,我想我可能已经找到了如何让这段代码工作的方法(基于),但我的问题是为什么会发生以下行为,这样我就可以理解将来不应该做什么了 我有三份档案。第一个是mod1.py:Python 导入模块:主模块与导入为模块,python,module,python-import,python-module,Python,Module,Python Import,Python Module,在前言中,我想我可能已经找到了如何让这段代码工作的方法(基于),但我的问题是为什么会发生以下行为,这样我就可以理解将来不应该做什么了 我有三份档案。第一个是mod1.py: # mod1.py import mod2 var1A = None def func1A(): global var1 var1 = 'A' mod2.func2() def func1B(): global var1 print var1 if __name__ == '
# 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()
如果执行命令
pythonmod1.py
,则输出为None
。基于我上面提到的链接,似乎在作为\uuuuu main\uuuu
导入的mod1.py
和从mod2.py
导入的mod1.py
之间存在一些区别。因此,我创建了driver.py
。如果执行命令python driver.py
,则得到预期的输出:A
。我有点明白其中的区别,但我并不真正了解其中的机制或原因。这是怎么发生的,为什么会发生?同一个模块会存在两次,这似乎违反直觉。如果我执行python mod1.py
,是否可以访问mod1.py
版本的\uuuuu main\uuuu
中的变量,而不是mod2.py
导入的版本中的变量?\uu name\uuu
变量始终包含模块的名称,除非文件已作为脚本加载到解释器中。然后将该变量设置为字符串“\uuuu main\uuuu”
毕竟,脚本作为整个程序的主文件运行,其他所有内容都是该主文件直接或间接导入的模块。通过测试\uuuu name\uuuu
变量,您可以检测文件是作为模块导入的,还是直接运行的
在内部,模块有一个名称空间字典,作为每个模块元数据的一部分存储在sys.modules
中。主文件,即已执行的脚本,存储在与“\uuuu main\uuuuu”
相同的结构中
但是,当您将文件作为模块导入时,python首先在sys.modules
中查看该模块之前是否已导入。因此,import mod1
意味着我们首先在sys.modules
中查找mod1
模块。如果mod1
还不存在,它将创建一个带有名称空间的新模块结构
因此,如果您都将mod1.py
作为主文件运行,然后将其作为python模块导入,它将在sys.modules
中获得两个名称空间条目。一个作为“main”
,然后作为“mod1”
。这两个名称空间是完全分开的。您的全局var1
存储在sys.modules['''''umain.']
中,但是func1B
正在sys.modules['mod1']
中查找var1
,其中它是None
但是,当您使用
python driver.py
时,driver.py
将成为程序的主文件,mod1
将只导入一次sys.modules['mod1']
结构。这一次,func1A
将var1
存储在sys.modules['mod1']
结构中,这就是func1B
将找到的。关于可选地将模块用作主脚本的实用解决方案-支持一致的交叉导入:
解决方案1:
例如,在Python的pdb模块中,请参见在作为\uuuuu main\uuuuu
执行时如何通过导入自身来作为脚本运行(最后):
我建议将\uuuu main\uuuu
启动重新组织到脚本的开头,如下所示:
#! /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:
在少数情况下,最好公开实际的脚本模块\uuuu main\uuuu
,甚至直接作为实际的模块别名(mod1
):
已知缺点:
重新加载(\u mod)
失败
- pickle'ed类需要额外的映射才能取消pickle(
find\u global
)
这样可以吗<代码>如果uuuu name_uuuuuu=''uuuuuuuu main_uuuuuuu':sys.modules['mod1']=sys.modules['''''uuuuuuu main_uuuuuuuu'];func1A()
@Kos:我不会那样做,我想你会发现很多假设都会被打破。避免使用模块作为脚本。@Brendan:约定使用tests.py
模块或tests
包,使用unittests
作为框架。@variable:因为调用了mod1.func1B()
,所以正在使用mod1
中函数的副本。函数在其自己的模块对象中使用全局变量。注意,还有一个\uuuuu main\uuuu.func1B()
函数,它连接到\uuuuuuu main\uuuu
全局变量。啊,我想我明白了。这里的问题是该值是在主命名空间中设置的。但是mod2函数是从mod1名称空间访问它的。如果你重构以消除循环导入,你会帮你自己一个忙。
#! /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
...
# 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()