首頁  >  文章  >  後端開發  >  在 Python 中修復循環導入的不同方法

在 Python 中修復循環導入的不同方法

Linda Hamilton
Linda Hamilton原創
2024-11-05 02:21:01784瀏覽

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