python中的循环引用

Guo*_*eng 3 python reference cycle

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

Python 的标准引用计数机制无法释放循环,因此示例中的结构会泄漏。
然而,补充垃圾收集工具默认启用,并且应该能够释放该结构,如果它的任何组件都不再可以从外部访问并且它们没有__del__()方法。

我想这意味着如果引用循环中的所有实例都无法在外部访问,则它们都将被清除。这是真的?
另一方面,有一个weakref通常用于处理地图字典的包。我想它存在的目的是为了避免引用循环。
综上所述,python可以自动处理引用循环吗?如果他们可以,为什么我们必须使用weakref

Art*_*yer 6

如果循环中的对象没有自定义__del__方法,则不必担心引用循环,因为 Python 可以(并且将)以任何顺序销毁对象。

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

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

a = Deletor('a')  # refcount: 1
del a             # refcount: 0
Run Code Online (Sandbox Code Playgroud)

输出:

a has been deleted
Run Code Online (Sandbox Code Playgroud)

但是如果你有这样的代码:

a = Deletor('a')  # refcount: 1
a.circular = a    # refcount: 2
del a             # refcount: 1
Run Code Online (Sandbox Code Playgroud)

它什么都不输出,因为 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
Run Code Online (Sandbox Code Playgroud)

输出:

b has been deleted
a has been deleted
Run Code Online (Sandbox Code Playgroud)

(注意如何b先删除再删除a

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

# refcount          a  b
a = Deletor('a')  # 1  0
b = Deletor('b')  # 1  1
b.a = a           # 2  1
a.b = b           # 2  2
del b             # 2  1
print('del b')
del a.b           # 2  0
# b is deleted, now dead
# b.a now dead    # 1  0
print('del a.b')
del a             # 0  0
print('del a')
Run Code Online (Sandbox Code Playgroud)

输出:

del b
b has been deleted
del a.b
a has been deleted
del a
Run Code Online (Sandbox Code Playgroud)

注意b删除 如何a.b删除。