python中的循环引用

python中的循环引用,python,reference,cycle,Python,Reference,Cycle,我不确定python如何处理循环引用(引用循环)。我检查了一些答案,发现: Python的标准引用计数机制无法释放周期,因此示例中的结构可能会泄漏。 但是,默认情况下会启用补充垃圾收集功能,如果从外部无法再访问它的任何组件,并且它们没有\uu del\uu()方法,那么它应该能够释放该结构 我猜这意味着,如果引用循环中的任何一个实例都不能在外部访问,那么它们都将被清除。这是真的吗? 另一方面,有一个包weakref,通常用于处理地图字典。我想,它的存在是为了避免引用循环。 总之,python能否

我不确定python如何处理循环引用(引用循环)。我检查了一些答案,发现:

Python的标准引用计数机制无法释放周期,因此示例中的结构可能会泄漏。
但是,默认情况下会启用补充垃圾收集功能,如果从外部无法再访问它的任何组件,并且它们没有
\uu del\uu()
方法,那么它应该能够释放该结构

我猜这意味着,如果引用循环中的任何一个实例都不能在外部访问,那么它们都将被清除。这是真的吗?
另一方面,有一个包
weakref
,通常用于处理地图字典。我想,它的存在是为了避免引用循环。

总之,python能否自动处理引用周期?如果可以,为什么我们必须使用
weakref

如果循环中的对象没有自定义的
\uu del\uuu
方法,那么您就不必担心引用循环,因为Python可以(也将)以任何顺序销毁对象

如果您的自定义方法确实有一个
\uuuu del\uuu
方法,那么Python根本不知道一个对象的删除是否会影响另一个对象的删除。比如说,当一个对象被删除时,它会设置一些全局变量。所以,物体会粘在周围。作为一个快速测试,您可以创建一个打印某些内容的
\uuu del\uu
方法:

class Deletor(str):
    def __del__(self):
        print(self, 'has been deleted')

a = Deletor('a')  # refcount: 1
del a             # refcount: 0
产出:

a has been deleted
b has been deleted
a has been deleted
del b
b has been deleted
del a.b
a has been deleted
del a
但如果您有这样的代码:

a = Deletor('a')  # refcount: 1
a.circular = a    # refcount: 2
del a             # refcount: 1
它不输出任何内容,因为Python无法安全地删除
a

有两种解决方案。
weakref

# refcount:             a  b
a = Deletor('a')      # 1  0
b = Deletor('b')      # 1  1
b.a = a               # 2  1
a.b = weakref.ref(b)  # 2  1
del a                 # 1  1
del b                 # 1  0
# del b kills b.a     # 0  0
产出:

a has been deleted
b has been deleted
a has been deleted
del b
b has been deleted
del a.b
a has been deleted
del a
(注意如何先删除
b
,然后才能删除
a

您可以手动删除周期(如果您可以跟踪它们):

产出:

a has been deleted
b has been deleted
a has been deleted
del b
b has been deleted
del a.b
a has been deleted
del a
注意
a.b
被删除后
b
是如何被删除的。

(试图回答为什么我们会有弱引用子问题。)

Weakrefs不仅打断循环引用,还防止不需要的非循环引用

 my_var=10 

我最喜欢的例子是使用
WeakSet
计算同时的网络连接(一种负载测量)。在本例中,每个新连接都必须添加到
WeakSet
,但这是网络代码需要完成的唯一任务。连接可以由服务器、客户端或错误处理程序关闭,但这些例程都没有责任从集合中删除连接,这是因为附加引用很弱。

变量是内存引用

 my_var=10 
这存储在其中一个内存插槽中
my_var
实际上引用存储10的内存插槽的地址。如果键入:

id(my_var) 
您将获得base-10中插槽的地址
hex(id(my_var))
将给出地址的十六进制表示

每当我们使用
my_var
python内存管理器时,它就会进入内存并检索10的值。Python内存管理器还跟踪此内存插槽的引用数。如果没有对该内存地址的引用,python内存管理器将销毁该对象,并将该内存槽用于新对象

假设我们有两个班:

class A:
    def __init__(self):
        self.b = B(self)
        print('A: self: {0}, b:{1}'.format(hex(id(self)), hex(id(self.b))))

class B:
    def __init__(self, a):
        self.a = a
        print('B: self: {0}, a: {1}'.format(hex(id(self)), hex(id(self.a))))
定义类别A的实例时:

   my_var = A()
您将得到打印:(在您的系统中,您将有不同的地址)

注意参考资料。它们是循环引用的

注意:为了查看这些引用,您必须禁用垃圾收集器,否则它将自动删除它们

  gc.disable()
当前my_var(0x1fc1eae4908)的引用计数为2。my_var和classB正在引用此地址。如果我们更改
my\u var

   my_var= None
现在
my_var
没有指向相同的内存地址。现在(0x1fc1eae4908)的引用计数为1,因此此内存插槽未清理

现在我们将出现内存泄漏,即不再需要内存时,不会清理内存

垃圾收集器将自动识别循环引用中的内存泄漏并将其清除。但是,即使循环引用中的一个对象具有析构函数(del()),垃圾收集器也不知道对象的销毁顺序。因此,对象被标记为无法收集,而循环引用中的对象未被清除,从而导致内存泄漏

weakref用于缓存目的。我认为python有非常好的文档

以下是weakref的参考: