为什么 Python 在使用 from-import 时对循环导入更加严格?
我知道 Python 不鼓励任何可能导致循环导入的情况。但我想了解 Python 的内部原理,为什么在循环导入的情况下,从导入看起来比普通导入更不宽容。
例如,此代码可以编译:
# main.py
import CommonUtil
# commonutil.py
import util
class CommonUtil:
# some code that uses util.Util
pass
# util.py
import commonutil
class Util:
# some code that uses commonutil.CommonUtil
pass
但此代码不会:
# main.py
import CommonUtil
# commonutil.py
import util
class CommonUtil:
# some code that uses util.Util
pass
# util.py
from commonutil import CommonUtil
class Util:
# some code that uses CommonUtil
pass
Traceback (most recent call last):
File "main.py", line 1, in <module>
import CommonUtil
File "commonutil.py", line 1, in <module>
import util
File "util.py", line 1, in <module>
from commonutil import CommonUtil
ImportError: cannot import name CommonUtil
只要在所有导入完成之前不尝试使用相关类,就不会遇到编译器错误。但是当你尝试做一些别名时,就会失败。有人能解释一下Python内部发生了什么导致这个错误只有在使用from-import时才会出现吗?其次,有什么简单的方法可以解决这个问题吗? (除了明显的“将共享代码拉出到第三个模块”之外,我无论如何都会这样做。)
I know that Python discourages any situation which can get you into a circular import. But I wanted understand the Python internals of why from-imports are seemingly arbitrarily less forgiving than normal imports in circular import situations.
For example, this code compiles:
# main.py
import CommonUtil
# commonutil.py
import util
class CommonUtil:
# some code that uses util.Util
pass
# util.py
import commonutil
class Util:
# some code that uses commonutil.CommonUtil
pass
But this code does not:
# main.py
import CommonUtil
# commonutil.py
import util
class CommonUtil:
# some code that uses util.Util
pass
# util.py
from commonutil import CommonUtil
class Util:
# some code that uses CommonUtil
pass
Traceback (most recent call last):
File "main.py", line 1, in <module>
import CommonUtil
File "commonutil.py", line 1, in <module>
import util
File "util.py", line 1, in <module>
from commonutil import CommonUtil
ImportError: cannot import name CommonUtil
You don't hit compiler errors as long as you don't try to use the relevant classes before all the imports have completed. But when you try to do some aliasing, then it fails. Can someone explain what's going on internally in the Python that causes this error to rear its head only when from-import is used? And secondarily, is there any easy way around this? (Besides the obvious "pull shared code out to a third module," which I'll likely do anyways.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
模块是从上到下执行的。当第一次看到导入时,当前模块的执行将被暂停,以便可以导入其他模块。当另一个模块尝试导入第一个模块时,它会获取对当前部分执行的模块的引用。由于导入其他模块之后的代码尚未执行,因此其中包含的任何名称尚不存在。
main.py
a.py
b.py
解决方法是在执行完成之前不要访问导入模块中的名称。
Modules are executed from top to bottom. When an import is seen for the first time, execution of the current module is suspended so that the other module can be imported. When the other module attempts to import the first module, it gets a reference to the currently partially-executed module. Since code located after the import of the other module hasn't yet been executed, any names contained within it cannot yet exist.
main.py
a.py
b.py
The way around it is to not access the names in the imported module until its execution has completed.
请参阅http://effbot.org/zone/import-confusion.htm#circular- import 来解释正在发生的事情。
我假设您启动了 main.py 文件。 Python 将首先尝试加载
commonutil
。它将创建一个模块对象,并在遇到它们的定义时开始用类、函数和全局变量填充它。第一条语句是导入,因此现在 python 创建 util 模块并开始填充它。公共模块存在,但为空。在第一个版本中,您在加载时不会访问任何 commonutil 对象,因此一切都很好。在第二个中,您尝试获取 commonutil 中目前不存在的特定变量。如果您在第一个版本中使用了类似f(commonutil.CommonUtil)
的内容,它也会崩溃。see http://effbot.org/zone/import-confusion.htm#circular-imports for an explanation of what is happening.
I assume you launch the main.py file. Python will first try to load
commonutil
. It will create a module object, and start filling it with class and function and global variable when encountering their definition. The first statement is an import, so now python creates theutil
module and start filling it. The common module exist, but is empty. In the first version, you do not access any commonutil object at load time, so everything is fine. In the second one, you try to fetch a specific variable in commonutil which does not exist at this moment. If you had used something likef(commonutil.CommonUtil)
in the first version, it would have also crashed.