Python 导入模块:主模块与导入为模块

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:

# 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()