如何处理Python类之间的循环依赖问题

如何处理Python类之间的循环依赖问题

在本文中,我们将讨论如何处理Python类之间的循环依赖问题。首先,让我们了解什么是循环依赖。

当两个或多个模块相互依赖时,这被称为 循环依赖 。这是因为每个模块都是基于另一个模块定义的。

下面是一个 循环依赖示例

functionE():
   functionF()

而且

functionF():
   functionE()

上面的代码清楚地显示了循环依赖。FunctionA()调用依赖于它的functionB(),而functionB()又调用functionA()。这种循环依赖有一些明显的问题,我们将在下一节详细讨论。

如何处理Python类之间的循环依赖问题

循环依赖的问题

循环依赖可能导致代码中的各种问题。例如,它可能导致模块之间紧密耦合,从而限制了代码的重用。这也使得长期代码维护更具挑战性。

循环依赖还可能引起内存泄漏、无限递归和级联效应等可能的问题。如果代码中存在循环依赖,这会导致许多可能的问题,而要解决这些问题可能非常具有挑战性,如果不小心处理的话。

示例

当涉及到循环引用的任何对象的类具有唯一的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中的导入语句会创建循环导入,一种循环依赖的形式。

示例

下面的示例解释了这个问题。假设我们创建了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()

示例3.py

# __init__.py
import module3
module3.func8()

Python在导入模块时会检查模块注册表,以查看是否已经导入了该模块。如果模块已经注册过,Python就会使用缓存中的先前存在的对象。模块注册表是一个已初始化的模块表,通过模块名称进行索引。 sys.modules 提供了访问该表的方式。

如果模块尚未注册,Python会定位它,并在必要时进行初始化,然后在新模块的命名空间中执行它。

在上面的示例中,当Python到达import module4时,它会加载并运行。但是,module4也被module3需要,因为它定义了func8()。

输出

问题出现在func4()尝试调用module3中的func9()时。由于func9()尚未定义并返回错误,因为module3先加载,且在其能够访问它之前加载了module4 –

$ python __init__.py
Thank You!
Traceback (most recent call last):
   File "__init__.py", line 3, in 
   Module3.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。

习惯上将所有导入语句插入模块(或脚本)的开头是常规做法,但并非必需,”这种方法不违反Python语法。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程