如何處理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