你在Python中遇到过循环导入吗?嗯,这是一种非常常见的代码味道,表明设计或结构有问题。
循环导入是如何发生的?当两个或多个相互依赖的模块在完全初始化之前尝试导入时,通常会发生此导入错误。
假设我们有两个模块:module_1.py 和 module_2.py。
# module_1.py from module_2 import ModY class ModX: mody_obj = ModY()
# module_2.py from module_1 import ModX class ModY: modx_obj = ModX()
在上面的代码片段中,module_1和module_2都是相互依赖的。
module_1 中 mody_obj 的初始化依赖于 module_2,module_2 中 modx_obj 的初始化依赖于 module_1。
这就是我们所说的循环依赖。两个模块在尝试互相加载时都会陷入导入循环。
如果我们运行 module_1.py,我们将得到以下回溯。
Traceback (most recent call last): File "module_1.py", line 1, in <module> from module_2 import ModY File "module_2.py", line 1, in <module> from module_1 import ModX File "module_1.py", line 1, in <module> from module_2 import ModY ImportError: cannot import name 'ModY' from partially initialized module 'module_2' (most likely due to a circular import)
这个错误说明了循环导入的情况。当程序尝试从 module_2 导入 ModY 时,当时 module_2 尚未完全初始化(由于另一个导入语句尝试从 module_1 导入 ModX)。
如何修复 Python 中的循环导入? 有多种方法可以消除 Python 中的循环导入。
我们可以将代码移动到一个公共文件中以避免导入错误,然后尝试从该文件导入模块。
# main.py ----> common file class ModX: pass class ModY: pass
在上面的代码片段中,我们将类 ModX 和 ModY 移动到一个公共文件 (main.py) 中。
# module_1.py from main import ModY class Mod_X: mody_obj = ModY()
# module_2.py from main import ModX class Mod_Y: modx_obj = ModX()
现在,module_1 和 module_2 从 main 导入类,修复了循环导入的情况。
这种方法有一个问题,有时代码库太大,将代码移动到另一个文件中会存在风险。
我们可以将导入语句移到模块末尾。这将为导入另一个模块之前留出时间来完全初始化模块。
# module_1.py class ModX: pass from module_2 import ModY class Mod_X: mody_obj = ModY()
# module_2.py class ModY: pass from module_1 import ModX
在类或函数作用域内导入模块可以避免循环导入。这允许仅在调用类或函数时导入模块。当我们想要最小化内存使用时,它是相关的。
# module_1.py class ModX: pass class Mod_X: from module_2 import ModY mody_obj = ModY()
# module_2.py class ModY: pass class Mod_Y: from module_1 import ModX modx_obj = ModX()
我们分别将 import 语句移至 module_1 和 module_2 中的类 Mod_X 和 Mod_Y 范围内。
如果我们运行 module_1 或 module_2,我们不会收到循环导入错误。但是,这种方法使得类只能在类的范围内访问,因此我们无法全局利用导入。
使用模块名称或仅像这样的别名可以解决问题。这允许两个模块通过将循环依赖推迟到运行时来完全加载。
# module_1.py from module_2 import ModY class ModX: mody_obj = ModY()
# module_2.py from module_1 import ModX class ModY: modx_obj = ModX()
我们还可以使用 importlib 库动态导入模块。
Traceback (most recent call last): File "module_1.py", line 1, in <module> from module_2 import ModY File "module_2.py", line 1, in <module> from module_1 import ModX File "module_1.py", line 1, in <module> from module_2 import ModY ImportError: cannot import name 'ModY' from partially initialized module 'module_2' (most likely due to a circular import)
# main.py ----> common file class ModX: pass class ModY: pass
通常,循环导入 来自同一包内的模块。在复杂的项目中,目录结构也很复杂,包内包。
这些包和子包包含 __init__.py 文件,以提供更轻松的模块访问。这就是有时无意中在模块之间产生循环依赖的地方。
我们有以下目录结构。
# module_1.py from main import ModY class Mod_X: mody_obj = ModY()
我们有一个 mainpkg 包和一个 main.py 文件。我们在 mainpkg 中有两个子包 modpkg_x 和 modpkg_y。
这是 modpkg_x 和 modpkg_y 中每个 Python 文件的样子。
mainpkg/modpkg_x/__init__.py
# module_2.py from main import ModX class Mod_Y: modx_obj = ModX()
此文件从 module_1 和 module_1_1 导入两个类(ModX 和 ModA)。
mainpkg/modpkg_x/module_1.py
# module_1.py class ModX: pass from module_2 import ModY class Mod_X: mody_obj = ModY()
module_1 从 module_2 导入 ModY 类。
mainpkg/modpkg_x/module_1_1.py
# module_2.py class ModY: pass from module_1 import ModX
module_1_1 没有导入任何内容。它不依赖于任何模块。
mainpkg/modpkg_y/__init__.py
# module_1.py class ModX: pass class Mod_X: from module_2 import ModY mody_obj = ModY()
此文件从 module_2 导入 ModY 类。
mainpkg/modpkg_y/module_2.py
# module_2.py class ModY: pass class Mod_Y: from module_1 import ModX modx_obj = ModX()
module_2 从 module_1_1 导入 ModA 类。
我们在 main.py 文件中有以下代码。
root_dir/main.py
# module_1.py import module_2 as m2 class ModX: def __init__(self): self.mody_obj = m2.ModY()
主文件从 module_2 导入 ModY 类。该文件依赖于 module_2。
如果我们在这里可视化导入周期,它看起来像下面忽略 modpkg_x 和 modpkg_y 中的 __init__.py 文件。
我们可以看到主文件依赖于module_2,module_1也依赖于module_2,module_2又依赖于module_1_1。没有导入周期。
但是你知道,模块依赖于它们的 __init__.py 文件,因此 __init__.py 文件首先初始化,然后重新导入模块。
这就是现在的导入周期。
这使得 module_1_1 依赖于 module_1,这是一个假依赖项。
如果是这种情况,清空子包 __init__.py 文件并使用单独的 __init__.py 文件可以帮助在包级别集中导入。
# module_1.py from module_2 import ModY class ModX: mody_obj = ModY()
在此结构中,我们在 mainpkg 中添加了另一个子包 subpkg。
mainpkg/subpkg/__init__.py
# module_2.py from module_1 import ModX class ModY: modx_obj = ModX()
这将允许内部模块从单一来源导入,从而减少交叉导入的需要。
现在我们可以更新 main.py 文件中的导入语句。
root_dir/main.py
Traceback (most recent call last): File "module_1.py", line 1, in <module> from module_2 import ModY File "module_2.py", line 1, in <module> from module_1 import ModX File "module_1.py", line 1, in <module> from module_2 import ModY ImportError: cannot import name 'ModY' from partially initialized module 'module_2' (most likely due to a circular import)
这解决了同一包内模块之间的循环依赖问题。
Python 中的循环依赖或导入是一种代码异味,这表明需要对代码进行认真的重构和重构。
您可以尝试上述任何一种方法来避免 Python 中的循环依赖。
?如果您喜欢这篇文章,您可能还会感兴趣
✅Flask 中的模板继承示例。
✅exec() 和 eval() 之间的区别并举例。
✅了解Python中global关键字的使用。
✅Python 类型提示:函数、返回值、变量。
✅为什么在函数定义中使用斜线和星号。
✅学习率如何影响 ML 和 DL 模型?
现在就这些。
继续编码✌✌。
以上是在 Python 中修复循环导入的不同方法的详细内容。更多信息请关注PHP中文网其他相关文章!