gpt4 book ai didi

python - python中的循环引用

转载 作者:太空宇宙 更新时间:2023-11-04 07:58:59 24 4
gpt4 key购买 nike

我不确定python是如何处理循环引用(reference cycle)的。我检查了一些答案并找到了 this :

Python's standard reference counting mechanism cannot free cycles, so the structure in your example would leak.
The supplemental garbage collection facility, however, is enabled by default and should be able to free that structure, if none of its components are reachable from the outside anymore and they do not have __del__() methods.

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

最佳答案

如果循环中的对象没有自定义 __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

输出:

a has been deleted

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

a = Deletor('a')  # refcount: 1
a.circular = a # refcount: 2
del a # refcount: 1

它不输出任何内容,因为 Python 不能安全地删除 a。它成为一个“不可收集的对象”,可以在gc.garbage中找到。 †

对此有两种解决方案。 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

输出:

b has been deleted
a has been deleted

(注意如何先删除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')

输出:

del b
b has been deleted
del a.b
a has been deleted
del a

注意 b 是如何在 a.b 被删除之后被删除的。


† 从 Python 3.4 开始,由于 PEP 442 而发生了变化. __del__ 甚至可以在引用循环中的对象上调用,并且语义略有不同,因此成为不可收集的对象稍微困难一些。

weakref 仍然有帮助,因为它对垃圾收集器的强度较低,并且可以更早地回收内存。

关于python - python中的循环引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43889268/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com