【问题标题】:Importing module from function in __init__.py binds module object to global namespace? [duplicate]从 __init__.py 中的函数导入模块会将模块对象绑定到全局命名空间? [复制]
【发布时间】:2019-01-11 17:29:20
【问题描述】:

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

【问题讨论】:

    标签: python python-2.7 python-import


    【解决方案1】:

    当你运行时:

    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 所描述的行为的实际问题,我没有找到答案,但对于任何其他模块都没有。似乎这种行为与 firstMod.py 是否首次导入无关。看看编辑。
    猜你喜欢
    • 1970-01-01
    • 2014-12-30
    • 2015-07-30
    • 1970-01-01
    • 2017-12-20
    • 2016-06-14
    • 1970-01-01
    • 2020-11-06
    • 2017-06-11
    相关资源
    最近更新 更多