我理解了第一部分。
问题是当您改为从foo导入abc和从bar导入xyz进行操作时。因为现在每个模块都需要先导入另一个模块(这样我们才能导入的名称已经存在)才能导入。
为什么答案说它会引起问题?
我理解了第一部分。
问题是当您改为从foo导入abc和从bar导入xyz进行操作时。因为现在每个模块都需要先导入另一个模块(这样我们才能导入的名称已经存在)才能导入。
为什么答案说它会引起问题?
加载模块时,就像任何对象一样,它分两个阶段完成。首先,分配内存,并创建最外部的数据结构(目前我们可以将其视为名称空间)。然后,里面的东西被依次初始化。
类似于import foo
中的bar.py
的语句在当前名称空间中分配名称foo
,并将其指向数据结构sys.modules['foo']
。即使尚未初始化该对象,该方法仍然有效,因为该对象至少是创建的,并且在没有任何机会对其进行调用之前,它已经被初始化了。
如果foo
包含语句import bar
,那么解释器要做的就是将当前部分初始化的bar
模块分配给{{1 }}的名称空间。同样,当任何东西尝试使用它时,bar
应该被完全初始化。
当您尝试循环导入特定的名称时,这会带来更多问题,因为您只能从已经初始化的模块中导入特定的名称。
假设您在foo
中有foo
,在from foo import baz
中有bar.py
。解释器会尝试完全加载from bar import qux
,因此可以确定foo.py
是哪个对象,但是要做到这一点,它需要foo
。但是要获取baz
,它需要完全加载qux
,以便可以弄清对象qux
是什么。但为此,它需要完全加载bar
,依此类推。这导致一个循环。
这的最终结果是您可以循环导入 entire模块,因为这样,解释器可以灵活地按顺序加载各自的内容,并且可以在任何东西调用之前可靠地初始化所有内容。模块。但是您不能循环地从模块中导入对象,因为解释器需要先加载整个模块,然后才能确定所需的对象。 The top answer for your linked question解释了此订购背后的技术流程。