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

时间:2016-05-14 23:21:09

标签: python list object reference garbage-collection

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

很容易跟踪这样的实例:

class A():
    instances = []
    def __init__(self):
        self.instances.append(self)

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

我尝试使用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

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

sys.getrefcount(DummyClass)

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

Copy = copy(A.instances)
del A.instances
A.instances = [i for i in Copy if not copy_of_i_is_in_GC(i)]

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

4 个答案:

答案 0 :(得分:7)

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

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

答案 1 :(得分: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)]

答案 2 :(得分:1)

试试gc.get_referrers(obj)The gc module Documentation

len(gc.get_referrers(my_obj))

答案 3 :(得分:1)

感谢@Barmar指出要使用 weakref 。我们可以将它与__del__方法结合起来,以实现类的自我管理实例列表。因此,OP帖子中的class A可以扩展为:

from weakref import ref
class A():
    instances = []
    def __init__(self):
        self.instances.append(ref(self))

    @staticmethod
    def __del__():
      if A:
        A.instances = [i for i in A.instances if not i() is None]

测试

#python2.7
print dict((len(A.instances), A()) for i in range(5)).keys() # 0,1,2,3,4
print len(A.instances) # 0

析构函数__del__可以声明为静态方法或对象绑定方法,如def __del__(self):,尽管没有记录。后者可以通过创建对它的另一个引用来阻止对象被破坏。这里我使用静态的,因为不需要另一个对dieing对象的引用。上面的代码在Python 2.7和3.3中进行了测试。

weakref.ref回调的行为类似于__del__,除了它绑定到“weakref”对象。因此,如果为具有相同回调函数的同一对象创建多个weakref,则将与weakrefs的数量完全相同地调用它。

相关问题