在Python中:如果只在列表中引用对象,如何从列表中删除它?

uzu*_*aki 16 python garbage-collection reference list object

我想跟踪当前正在使用的某种类型的对象.例如:跟踪类的所有实例或由元类创建的所有类.

跟踪这样的实例很容易:

class A():
    instances = []
    def __init__(self):
        self.instances.append(self)
Run Code Online (Sandbox Code Playgroud)

但是如果一个实例没有被引用到该列表之外的任何地方,则不再需要它,我不想在可能耗时的循环中处理该实例.

我尝试使用sys.getrefcount删除仅在列表中引用的对象.

for i in A.instances:
    if sys.getrefcount(i) <=3: # in the list, in the loop and in getrefcount
        # collect and remove after the loop
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是引用计数非常模糊.打开一个新的shell并创建一个没有内容的虚拟类,返回5

sys.getrefcount(DummyClass)
Run Code Online (Sandbox Code Playgroud)

另一个想法是复制对象然后删除列表并检查哪些对象已被安排用于垃圾收集,并在最后一步中删除这些对象.就像是:

Copy = copy(A.instances)
del A.instances
A.instances = [i for i in Copy if not copy_of_i_is_in_GC(i)]
Run Code Online (Sandbox Code Playgroud)

当引用计数变为0时,不必立即删除对象.我只是不想在不再使用的对象上浪费太多的资源.

Kev*_*vin 7

解决这个问题的标准方法是通过弱引用.基本思想是保留对象的弱引用列表而不是对象本身,并定期从列表中删除死弱引用.

对于字典和集合,有一些更抽象的类型,例如,weakref.WeakKeyDictionary()当您想要将弱引用放在更复杂的位置(如字典的键)时,可以使用这些类型.这些类型不需要手动修剪.


tde*_*ney 7

这个答案与凯文的答案相同,但我正在编写一个带有弱引用的示例实现,并在此处发布.使用弱引用可以解决self.instance列表引用对象的问题,因此永远不会删除它.

为对象创建弱引用的一个方面是,可以在删除对象时包含回调.有一些问题,例如当程序退出时没有发生回调......但这可能是你想要的.

import threading
import weakref

class A(object):
    instances = []
    lock = threading.RLock()

    @classmethod
    def _cleanup_ref(cls, ref):
        print('cleanup') # debug
        with cls.lock:
            try:
                cls.instances.remove(ref)
            except ValueError:
                pass

    def __init__(self):
        with self.lock:
            self.instances.append(weakref.ref(self, self._cleanup_ref))

# test
test = [A() for _ in range(3)]
for i in range(3,-1,-1):
    assert len(A.instances) == i
    if test:
        test.pop()

print("see if 3 are removed at exit")
test = [A() for _ in range(3)]
Run Code Online (Sandbox Code Playgroud)