首页 后端开发 Python教程 在 Python 中修复循环导入的不同方法

在 Python 中修复循环导入的不同方法

Nov 05, 2024 am 02:21 AM

你在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 中的循环导入。

修复 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 库

我们还可以使用 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
登录后复制
登录后复制

Python 包中的循环导入

通常,循环导入 来自同一包内的模块。在复杂的项目中,目录结构也很复杂,包内包。

这些包和子包包含 __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 文件。

Different Ways to Fix Circular Imports in Python

我们可以看到主文件依赖于module_2,module_1也依赖于module_2,module_2又依赖于module_1_1。没有导入周期。

但是你知道,模块依赖于它们的 __init__.py 文件,因此 __init__.py 文件首先初始化,然后重新导入模块。

Different Ways to Fix Circular Imports in Python

这就是现在的导入周期。

Different Ways to Fix Circular Imports in Python

这使得 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中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

如何解决Linux终端中查看Python版本时遇到的权限问题? 如何解决Linux终端中查看Python版本时遇到的权限问题? Apr 01, 2025 pm 05:09 PM

Linux终端中查看Python版本时遇到权限问题的解决方法当你在Linux终端中尝试查看Python的版本时,输入python...

如何在使用 Fiddler Everywhere 进行中间人读取时避免被浏览器检测到? 如何在使用 Fiddler Everywhere 进行中间人读取时避免被浏览器检测到? Apr 02, 2025 am 07:15 AM

使用FiddlerEverywhere进行中间人读取时如何避免被检测到当你使用FiddlerEverywhere...

在Python中如何高效地将一个DataFrame的整列复制到另一个结构不同的DataFrame中? 在Python中如何高效地将一个DataFrame的整列复制到另一个结构不同的DataFrame中? Apr 01, 2025 pm 11:15 PM

在使用Python的pandas库时,如何在两个结构不同的DataFrame之间进行整列复制是一个常见的问题。假设我们有两个Dat...

Uvicorn是如何在没有serve_forever()的情况下持续监听HTTP请求的? Uvicorn是如何在没有serve_forever()的情况下持续监听HTTP请求的? Apr 01, 2025 pm 10:51 PM

Uvicorn是如何持续监听HTTP请求的?Uvicorn是一个基于ASGI的轻量级Web服务器,其核心功能之一便是监听HTTP请求并进�...

如何在10小时内通过项目和问题驱动的方式教计算机小白编程基础? 如何在10小时内通过项目和问题驱动的方式教计算机小白编程基础? Apr 02, 2025 am 07:18 AM

如何在10小时内教计算机小白编程基础?如果你只有10个小时来教计算机小白一些编程知识,你会选择教些什么�...

在Linux终端中使用python --version命令时如何解决权限问题? 在Linux终端中使用python --version命令时如何解决权限问题? Apr 02, 2025 am 06:36 AM

Linux终端中使用python...

如何绕过Investing.com的反爬虫机制获取新闻数据? 如何绕过Investing.com的反爬虫机制获取新闻数据? Apr 02, 2025 am 07:03 AM

攻克Investing.com的反爬虫策略许多人尝试爬取Investing.com(https://cn.investing.com/news/latest-news)的新闻数据时,常常�...

See all articles