首頁 後端開發 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