在 Python 中,循环导入是指两个文件各自尝试导入另一个文件, 当模块未完全初始化时导致失败, 最好的修复方法 将代码分层组织,以便导入,但有时它可以只需更改使用的 import 语句的样式即可。
假设有以下文件:
# one.pyfrom two import func_twodef func_one():func_two()
# two.pyfrom one import func_onedef do_work():func_one()def func_two():print("Hello, world!")
# main.pyfrom two import do_workdo_work()
如果我们运行 main.py,我们会得到以下结果:
% python main.pyTraceback (most recent calllast):File"main.py", line 2, in <module>from two import do_workFile"two.py", line 2, in <module>from one import func_oneFile"one.py", line 2, in <module>from two import func_twoImportError: cannot importname'func_two'from partially initializedmodule'two' (most likely due to a circular import) (two.py)
当 Python 导入模块时,它会逐行执行文件,文件中的每个全局变量(包括函数和类的顶级名称)都成为正在构造的模块对象的属性,在 two.py 中,我们在第 2 行从 one.py 导入,此时,模块已创建,但尚未定义任何属性,但我们尚未执行那些 def 语句,因此它们不存在,与函数调用一样,当运行 import 语句时,它开始执行导入的文件,直到导入完成才会返回当前文件。
one.py 的导入开始,其第 2 行尝试从 two 模块中获取名称,正如我们刚才所说,模块存在,但尚未定义名称,这给了我们错误。
我们可以导入整个模块,而不是从模块导入名称。我们所做的就是更改导入的形式,以及我们如何从导入的模块中引用函数,如下所示:
# one.pyimport twodef func_one():two.func_two()
# two.pyimport onedef do_work():one.func_one()def func_two():print("Hello, world!")
# main.pyfrom two import do_workdo_work()
运行代码,我们得到:
% python main.pyHello, world!
它之所以有效是因为 two.py 在第 2 行导入,然后 one.py 在其第 2 行导入,因为模块存在。它仍然像修复之前一样是空的,但现在我们不会尝试在导入期间在其中查找名称,一旦完成所有导入,和模块都定义了所有名称,我们可以从函数内部访问它们。
这里的关键思想是“from two import func_two”在导入期间尝试查找,在它存在之前。通过使用“import two”将名称查找推迟到函数主体,可以让所有模块在我们尝试使用它们之前完全初始化,从而避免循环导入错误。
正如我在开始提到的,修复循环导入的最佳方法是构建代码,使模块没有这样的相互依赖关系,但这并不总是那么容易,这可以为你赢得一点时间让你的代码重新工作。
长按或扫描下方二维码,免费获取 Python公开课和大佬打包整理的几百G的学习资料,内容包含但不限于Python电子书、教程、项目接单、源码等等 推荐阅读
点击 阅读原文 了解更多