导入模块:__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.pymod2.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__'] 中,但func1Bsys.modules['mod1'] 中查找var1,它是None

但是当你使用python driver.py时,driver.py变成了程序的'__main__'主文件,mod1只会被导入到sys.modules['mod1']结构中一次。这一次,func1Avar1 存储在 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的主要内容,如果未能解决你的问题,请参考以下文章

import包和模块_2

Python模块导入和常用内置方法

python中的if __name__ == '__main__' what hell is it?

Python学习笔记

关于import和from xx import xx

基础入门_Python-模块和包.运维开发中__import__动态导入最佳实践?