从__init__.py中的函数导入模块将模块对象绑定到全局命名空间?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从__init__.py中的函数导入模块将模块对象绑定到全局命名空间?相关的知识,希望对你有一定的参考价值。

我是Python的初学者,我对模块对象如何绑定到包的__init__.py名称空间有疑问。我会用一些代码更清楚地说明问题。


假设有一个名为myPkg的包,包含两个模块:firstModsecondMod

myPkg
     __init__.py
     firstMod.py
     secondMod.py

文件__init__.py如下:

def myFun():
    from . import firstMod as fm

myFun()

文件firstMod为空。

文件secondMod如下:

def myFun():
    from . import firstMod as fm

myFun()

现在,在与myPkg相同的目录中运行Python 2.7.15解释器,并执行以下操作:

>>> import myPkg
>>> dir(myPkg)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'firstMod', 'myFun']
>>> from myPkg import secondMod
>>> dir(secondMod)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'myFun']
>>>

虽然dir(secondMod)的结果是预期的,但是dir(myPkg)的结果出乎意料(对我而言)。似乎只是对于包的__init__.py模块,导入的模块对象的名称绑定到全局命名空间,即使模块是从函数中导入的。其他模块中不会发生这种情况。

编辑:事实证明,这只发生在导入包的模块时。从myFun导入外部模块不会导致__init__.py中的名称绑定。


谁能解释为什么会这样?而且:有没有办法避免这种行为?


编辑

请注意,'firstMod'全局命名空间中__init__.py的存在是一个仅适用于包的属性。

实际上,如果定义了两个模块:

zeroMod.py
firstMod.py

在任何包装之外,填充zeroMod.py

def myFun():
    import firstMod as fm

myFun()

解释器不会将名称'firstMod'绑定到zeroMod.py的全局命名空间,即使第一次加载firstMod.py

>>> import zeroMod
>>> dir(zeroMod)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'myFun']
>>>

我不明白为什么名字绑定发生在__init__.py而不是zeroMod.py

答案

当你运行:

import myPkg

Python将加载文件myPkg/__init__.py并执行它。

因为,这个包包含一个函数调用(myFun()),它将执行它(在第一次导入时只执行一次)。

myFun()是:

def myFun():
    from . import firstMod as fm

再次,此函数导入firstMod。因此加载并执行文件myPkg/firstMod.py(因为firstMod.py为空,所以没有任何操作)。

myPkg导入结束时,您将在模块级别获得以下对象:

['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'firstMod', 'myFun']

然后,在同一个Python会话中,如果运行:

from myPkg import secondMod

Python不会再次加载文件myPkg/__init__.py,因为模块已经加载。

Python将加载文件myPkg/secondMod.py并执行它。

这个包包含另一个函数调用(myFun()),它也会执行它。请注意,您使用相同的名称但在不同的命名空间中,因此两个myFun()函数是不同的。

myFun()也导入firstMod

def myFun():
    from . import firstMod as fm

firstMod已经导入,所以你不会在你的secondMod模块中找到它:

['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'myFun']

你应该考虑的事情:

  • 你应该考虑遵循PEP8 naming conventions
  • 模块是单件(即使在极少数情况下你可以重新加载模块),
  • 如果您不想在模块中运行代码,请使用经典方法:

例如:

if __name__ == "__main__":
    myFun()

以上是关于从__init__.py中的函数导入模块将模块对象绑定到全局命名空间?的主要内容,如果未能解决你的问题,请参考以下文章

python基础:__init__.py和__init__函数的作用

在 Python 和 __init__.py 中导入模块

如何在 Python 中正确导入模块

第十五天:模块

Python __init__.py 作用详解

Python __init__.py 作用详解