循环导入的命名空间

Namespace of circular import

当shellimport模块one时,我以为one会导入two 和 运行 two 的代码,因此它会自动导入 three。这样,三个模块的名称将全部包含在dir()中。

实际上,当我检查命名空间时,只有模块 one(见下文)。

如何在不包括模块 twothree 的命名空间的情况下打印 'Hello'?由于 one.f1() 依赖于这两个模块。

>>> ================================ RESTART ================================
>>> import one
in three
In two
in one
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'one']
>>> one.f1()
Hello
>>> 

'one.py'
import two
print('in one')

def f1():
    two.f2()

def f4():
    print ('Hello')

'two.py'
import three
print ('In two')

def f2():
    three.f3()

'three.py'
import one
print('in three')
def f3():
    one.f4()

每个模块都有自己的自己的命名空间,包括主脚本。您只将模块 one 导入主脚本命名空间。

其他模块分别导入到模块 one 和模块 two 的命名空间中。

事实上,仅导入意味着您在当前命名空间中创建了对导入项(此处为模块对象)的引用。 Python 将确保首先加载模块(这是当您看到正在执行的 print() 语句时),然后实际导入发生(当前命名空间中的名称绑定)。

因此,语句 import one 做了两件事:

  • 查找并加载模块 one一次,如果尚未加载。这包括创建模块并将名称插入其中。
  • 将当前命名空间中的名称 one 绑定到模块。

模块存储在sys.modules;它们在加载开始时存储在那里。

在您的情况下,模块 one 的加载会触发另一个导入,因此 Python 加载模块 two 以满足该导入。这反过来会触发模块 three 的导入。 three 导入 one,模块 已经存在于 sys.modules 中。因此名称 one 不存在问题,因为已经有一个模块可以绑定。该模块仍然是空的(因为它仍在加载过程中)。

事件的完整顺序是:

  • import one -> sys.modules 中没有这样的模块
    • 创建sys.modules['one'],加载代码并开始执行
    • import two -> sys.modules
    • 中没有这样的模块
    • 创建sys.modules['two'],加载代码并开始执行
    • import three -> sys.modules 中没有这样的模块
      • 创建sys.modules['three'],加载代码并开始执行
      • import one -> sys.modules['one'] 存在,绑定 onesys.modules['one'].
      • print('in three')
      • 创建函数 f3.
      • 加载完成 three
    • three 绑定到 sys.modules['three']
    • print ('In two')
    • 创建函数f2
    • 加载完成 two
    • two 绑定到 sys.modules['two']
    • print('in one')
    • 创建函数 f1f4
    • 加载完成 one
  • 执行one.f1()
    • one.f1() 存在并被执行 -> two.f2()
    • two.f2() 存在并被执行 -> three.f3()
      • three.f3() 存在并被执行 -> one.f4()
      • one.f4() 存在并被执行 > print('Hello')

在创建 three.f3()one.f4() 尚未 存在这一事实在这里无关紧要;只有在执行函数时才会查找名称。