你在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中文網其他相關文章!