循环导入的命名空间
Namespace of circular import
当shellimport
模块one时,我以为one会导入two 和 运行 two 的代码,因此它会自动导入 three。这样,三个模块的名称将全部包含在dir()
中。
实际上,当我检查命名空间时,只有模块 one(见下文)。
如何在不包括模块 two 和 three 的命名空间的情况下打印 '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']
存在,绑定 one
到 sys.modules['one']
.
print('in three')
- 创建函数
f3
.
- 加载完成
three
- 将
three
绑定到 sys.modules['three']
print ('In two')
- 创建函数
f2
- 加载完成
two
- 将
two
绑定到 sys.modules['two']
print('in one')
- 创建函数
f1
和 f4
- 加载完成
one
- 执行
one.f1()
one.f1()
存在并被执行 -> two.f2()
two.f2()
存在并被执行 -> three.f3()
three.f3()
存在并被执行 -> one.f4()
one.f4()
存在并被执行 > print('Hello')
在创建 three.f3()
时 one.f4()
尚未 存在这一事实在这里无关紧要;只有在执行函数时才会查找名称。
当shellimport
模块one时,我以为one会导入two 和 运行 two 的代码,因此它会自动导入 three。这样,三个模块的名称将全部包含在dir()
中。
实际上,当我检查命名空间时,只有模块 one(见下文)。
如何在不包括模块 two 和 three 的命名空间的情况下打印 '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']
存在,绑定one
到sys.modules['one']
.print('in three')
- 创建函数
f3
. - 加载完成
three
- 创建
- 将
three
绑定到sys.modules['three']
print ('In two')
- 创建函数
f2
- 加载完成
two
- 将
two
绑定到sys.modules['two']
print('in one')
- 创建函数
f1
和f4
- 加载完成
one
- 创建
- 执行
one.f1()
one.f1()
存在并被执行 ->two.f2()
two.f2()
存在并被执行 ->three.f3()
three.f3()
存在并被执行 ->one.f4()
one.f4()
存在并被执行 >print('Hello')
在创建 three.f3()
时 one.f4()
尚未 存在这一事实在这里无关紧要;只有在执行函数时才会查找名称。