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

时间:2019-01-11 17:29:21

标签: python-2.7 python-import

我是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不会发生名称绑定。

1 个答案:

答案 0 :(得分:0)

运行时:

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