如何處理 Python 類之間的迴圈依賴?
在本文中,我們將討論如何處理 Python 類之間的迴圈依賴。首先,讓我們瞭解什麼是迴圈依賴。
當兩個或多個模組相互依賴時,這被稱為迴圈依賴。這是因為每個模組都是根據另一個模組定義的。
以下是迴圈依賴的示例
functionE(): functionF()
以及
functionF(): functionE()
上面顯示的程式碼清楚地顯示了迴圈依賴。FunctionA() 呼叫 functionB(),後者依賴於它,而 functionB() 呼叫 functionA()。這種迴圈依賴有一些明顯的問題,我們將在下一節中更詳細地討論。

迴圈依賴的問題
迴圈依賴可能會導致程式碼出現各種問題。例如,它可能導致模組之間緊密耦合,這將限制程式碼重用。這方面也使長期程式碼維護更具挑戰性。
迴圈依賴也可能是記憶體洩漏、無限遞迴和級聯效應等問題的根源。當您的程式碼包含迴圈依賴時,解決它產生的許多可能問題可能非常具有挑戰性,如果您不小心,這種情況可能會發生。
示例
當參與迴圈引用的任何物件的類具有唯一的 __del__ 函式時,就會出現問題。以下是一個顯示迴圈依賴出現問題的示例:
class Python: def __init__(self): print("Object Python is Created") def __del__(self): print("Object Python is Destroyed") class Program: def __init__(self): print("Object Program is Created") def __del__(self): print("Object Program is Destroyed") #create the two objects Py = Python() Pr = Program() #set up the circular reference Py.Pr = Pr Pr.Py = Py #delete the objects del Py del Pr
輸出
在這裡,Py 和 Pr 物件都有一個自定義的 __del__ 函式,並且相互持有引用。最終,當我們嘗試手動刪除物件時,__del__ 方法沒有被呼叫,這表明物件沒有被銷燬,而是導致了記憶體洩漏。
在這種情況下,Python 的垃圾收集器無法收集物件以進行記憶體清理,因為它不確定以什麼順序呼叫 __del__ 函式。
Object Python is Created Object Program is Created Object Python is Destroyed Object Program is Destroyed
修復 Python 中迴圈依賴中的記憶體洩漏
迴圈引用會導致記憶體洩漏,可以透過兩種方式避免,即手動清除每個引用和使用 Python 中的weakref() 函式。
手動刪除每個引用不是一個理想的選擇,因為weakref() 消除了程式設計師需要考慮刪除引用的時間的需要。
在 Python 中,weakref 函式提供了一個弱引用,它不足以維持物件的生存期。當對物件的唯一剩餘引用是弱引用時,該物件可以被垃圾收集器自由銷燬,以便其記憶體可以被另一個物件使用。
示例
以下示例顯示瞭如何修復迴圈依賴中的記憶體洩漏:
import weakref class Python: def __init__(self): print("Object Python is Created") def __del__(self): print("Object Python is Destroyed") class Program: def __init__(self): print("Object Program is Created") def __del__(self): print("Object Program is Destroyed") #create the two objects Py = Python() Pr = Program() #set up the weak circular reference Py.Pr = weakref.ref(Pr) Pr.Py = weakref.ref(Py) #delete the objects del Py del Pr
輸出
正如您所看到的,這次使用了兩種 __del__ 方法,證明物件已成功從記憶體中刪除。
Object Python is Created Object Program is Created Object Python is Destroyed Object Program is Destroyed
透過迴圈匯入的迴圈依賴
Python 中的 import 語句會建立迴圈匯入,這是一種迴圈依賴。
示例
以下示例說明了這一點。假設我們建立了 3 個 Python 檔案,如下所示:
Example1.py
# module3 import module4 def func8(): module4.func4() def func9(): print('Welcome to TutorialsPoint')
Example2.py
# module4 import module4 def func4(): print('Thank You!') module3.func9()
Example3.py
# __init__.py import module3 module3.func8()
Python 在匯入模組時檢查模組登錄檔以檢視它是否已被匯入。如果模組已被註冊,Python 將使用快取中先前存在的物件。模組登錄檔是一個已初始化模組的表,使用模組名稱作為索引。sys.modules 提供對該表的訪問。
如果模組未註冊,Python 會找到它,根據需要初始化它,然後在新模組的名稱空間中執行它。
在上面的示例中,Python 在到達 import module4 時載入並執行。但是,module3 也需要 module4,因為它定義了 func8()。
輸出
當 func4() 嘗試呼叫 module3 中的 func9() 時,問題就出現了。func9() 尚未定義並返回錯誤,因為 module3 先載入,它在訪問它之前載入了 module4:
$ python __init__.py Thank You! Traceback (most recent call last): File "__init__.py", line 3, inModule3.func8() File "C:\Users\Lenovo\Desktop\module3\__init__.py", line 5, in func8 Module4.func4() File "C:\Users\Lenovo\Desktop\module4\__init__.py", line 6, in func4 module4.func9() AttributeError: 'module' object has no attribute 'func9
修復上述迴圈依賴
迴圈匯入通常是不良設計的結
有時,將兩個模組合併到一個更大的模組中是一個簡單的選擇。
示例
來自上述示例的最終程式碼將類似於上面給出的解釋:
# module 3 & 4 def func8(): func4() def func4(): print('Welcome to TutorialsPoint') func9() def func9(): print('Thank You!') func8()
輸出
以下是上述程式碼的輸出:
Welcome to TutorialsPoint Thank You!
注意 - 但是,如果兩個模組已經包含大量程式碼,合併後的模組可能包含一些不相關的函式(緊密耦合),並且可能會大幅增長。
如果它不起作用,另一種選擇可能是延遲匯入 module4,只在需要時匯入它。為此,請將 module4 的匯入包含在 func8() 的定義中,如下所示:
# module 3 def func8(): import module4 module4.func4() def func9(): print('Thank You!')
在上述情況下,Python 將能夠載入 module3 中的所有函式,並且僅在需要時載入 module4。
通常的做法是在模組(或指令碼)的開頭插入所有 import 語句,但這不是必需的,“這種方法不會違反 Python 語法。
資料結構
網路
關係資料庫管理系統 (RDBMS)
作業系統
Java
iOS
HTML
CSS
Android
Python
C 程式設計
C++
C#
MongoDB
MySQL
Javascript
PHP