为了前言,我想我可能已经找到了如何让这段代码的工作(根据更改后导入模块的变量 ),但我的问题是真的,为什么会出现以下行为,所以我可以理解将来不会做什么。
我有三个文件。 首先是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
?
该__name__
变量总是包含模块,当文件被加载到解释为脚本,而不是除了名。 然后该变量被设置为字符串'__main__'
来代替。
毕竟,脚本然后运行作为整个程序的主文件,其他的一切都是由主文件直接或间接导入的模块。 通过测试__name__
变量,因此您能检测一个文件已经导入的模块,或者直接运行。
在内部,模块给出一个命名空间的字典,其被存储为每个模块的元数据的一部分,在sys.modules
。 主文件,执行的脚本,被存储在相同的结构'__main__'
。
但是,当你输入一个文件作为一个模块,蟒蛇首先查找在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
会发现。
关于用于使用模块可选地作为主脚本的实际解决方案 - 支撑一致的横进口:
解决方案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'ed类将需要额外的映射取储存(
find_global
..)