我应该担心Python中的循环引用吗?

我应该担心Python中的循环引用吗?,python,Python,假设我有维护父/子结构的代码。在这种结构中,我得到循环引用,其中子对象指向父对象,父对象指向子对象。我应该担心他们吗?我正在使用Python 2.5 我担心它们不会被垃圾收集,应用程序最终会消耗所有内存。Python将检测循环,并在没有外部引用时释放内存。实验性地:您很好: import itertools for i in itertools.count(): a = {} b = {"a":a} a["b"] = b 它始终保持在使用3.6MB内存的状态。循环引用

假设我有维护父/子结构的代码。在这种结构中,我得到循环引用,其中子对象指向父对象,父对象指向子对象。我应该担心他们吗?我正在使用Python 2.5


我担心它们不会被垃圾收集,应用程序最终会消耗所有内存。

Python将检测循环,并在没有外部引用时释放内存。

实验性地:您很好:

import itertools

for i in itertools.count():
    a = {}
    b = {"a":a}
    a["b"] = b

它始终保持在使用3.6MB内存的状态。

循环引用是一件正常的事情,所以我不认为有理由担心它们。许多树算法要求每个节点都有到其子节点和父节点的链接。它们还需要实现类似双链接列表的功能。

我认为您不必担心。尝试以下程序,您会发现它不会消耗所有内存:

while True:
    a=range(100)
    b=range(100)
    a.append(b)
    b.append(a)
    a.append(a)
    b.append(b)
“担心”放错了地方,但如果你的程序运行缓慢,消耗的内存超过预期,或者出现奇怪的无法解释的暂停,那么原因很可能确实在那些垃圾引用循环中——它们需要通过与“正常”(非循环)引用图不同的过程进行垃圾收集,这种收集是偶然的,如果在这样的循环中有很多对象,那么收集可能会很慢(如果循环中的对象具有
\uu del\uu
特殊方法,那么循环垃圾收集也会被禁止)

因此,引用循环不会影响程序的正确性,但可能会影响其性能和/或占用空间

如果要删除不需要的引用循环,通常可以使用Python标准库中的模块


如果您想对循环垃圾收集实施更直接的控制(或执行调试,查看具体发生了什么),请使用Python标准库中的模块。

变量列表中的方法引用似乎存在问题。这里有两个例子。第一个不调用
\uu del\uu
。带weakref的第二个对于
\uu del\uu
来说是可以的。但是,在后一种情况下,问题在于不能弱引用方法:


谢谢你,科林。我不知道它们是“正常的东西”,它们对我来说很特别。但现在我学到了另一种方法当然,假设没有
\uu del\uu
方法。这通常是不应该的,但你永远不知道。有一段时间,甚至
collections.OrderedDict
也有一个是出于某种原因。你是说
a.extend(b)
,而不是
append
?@richizy我真正的意思是append,因为我想在a和b中保存对a和b的引用,而不是值。这样循环引用就会发生。您使用了哪个实现?@SargeBorsch CPython 2.something。我想任何主要的实现都会以同样的方式运行。关于
的注释加上1。如果对象析构函数有副作用,那么您可能需要更仔细地考虑循环引用(以及当事物被破坏时)。
import sys, weakref

class One():
    def __init__(self):
        self.counters = [ self.count ]
    def __del__(self):
        print("__del__ called")
    def count(self):
        print(sys.getrefcount(self))


sys.getrefcount(One)
one = One()
sys.getrefcount(One)
del one
sys.getrefcount(One)


class Two():
    def __init__(self):
        self.counters = [ weakref.ref(self.count) ]
    def __del__(self):
        print("__del__ called")
    def count(self):
        print(sys.getrefcount(self))


sys.getrefcount(Two)
two = Two()
sys.getrefcount(Two)
del two
sys.getrefcount(Two)