搜尋
首頁後端開發Python教學在 Python 中修復循環導入的不同方法

你在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></module></module>

這個錯誤說明了循環導入的情況。當程式嘗試從 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)
</module></module></module>
# 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)
</module></module></module>

這解決了同一包內模組之間的循環相依性問題。

結論

Python 中的循環依賴或導入是一種程式碼異味,這表明需要對程式碼進行認真的重構和重構。

您可以嘗試上述任何一種方法來避免 Python 中的循環依賴。


如果您喜歡這篇文章,您可能也會感興趣

✅Flask 中的範本繼承範例。

✅exec() 和 eval() 之間的差異並舉例。

✅了解Python中global關鍵字的使用。

✅Python 類型提示:函數、傳回值、變數。

✅為什麼在函數定義中使用斜線和星號。

✅學習率如何影響 ML 與 DL 模型?


現在就這些。

繼續編碼✌✌。

以上是在 Python 中修復循環導入的不同方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python中有可能理解嗎?如果是,為什麼以及如果不是為什麼?Python中有可能理解嗎?如果是,為什麼以及如果不是為什麼?Apr 28, 2025 pm 04:34 PM

文章討論了由於語法歧義而導致的Python中元組理解的不可能。建議使用tuple()與發電機表達式使用tuple()有效地創建元組。 (159個字符)

Python中的模塊和包裝是什麼?Python中的模塊和包裝是什麼?Apr 28, 2025 pm 04:33 PM

本文解釋了Python中的模塊和包裝,它們的差異和用法。模塊是單個文件,而軟件包是帶有__init__.py文件的目錄,在層次上組織相關模塊。

Python中的Docstring是什麼?Python中的Docstring是什麼?Apr 28, 2025 pm 04:30 PM

文章討論了Python中的Docstrings,其用法和收益。主要問題:Docstrings對於代碼文檔和可訪問性的重要性。

什麼是lambda功能?什麼是lambda功能?Apr 28, 2025 pm 04:28 PM

文章討論了Lambda功能,與常規功能的差異以及它們在編程方案中的效用。並非所有語言都支持他們。

什麼是休息時間,繼續並通過python?什麼是休息時間,繼續並通過python?Apr 28, 2025 pm 04:26 PM

文章討論了休息,繼續並傳遞Python,並解釋了它們在控制循環執行和程序流中的作用。

Python的通行證是什麼?Python的通行證是什麼?Apr 28, 2025 pm 04:25 PM

本文討論了Python中的“ Pass”語句,該語句是函數和類等代碼結構中用作佔位符的空操作,允許在沒有語法錯誤的情況下實現將來實現。

我們可以在Python中傳遞作為參數的函數嗎?我們可以在Python中傳遞作為參數的函數嗎?Apr 28, 2025 pm 04:23 PM

文章討論了將功能作為Python中的參數,突出了模塊化和用例(例如分類和裝飾器)等好處。

Python中的 /和//有什麼區別?Python中的 /和//有什麼區別?Apr 28, 2025 pm 04:21 PM

文章在Python中討論 /和//運營商: / for for True Division,//用於地板部門。主要問題是了解它們的差異和用例。 Character數量:158

See all articles

熱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

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。