3

我正在尝试使用静态列表创建一个类,该列表收集对象类的所有新实例。我面临的问题似乎是,一旦我尝试以与整数相同的方式使用列表,我就不能再使用魔术标记__del__了。

我的例子:

class MyClass(object):  

    count = 0
    #instances = []

    def __init__(self, a, b):
        self.a = a
        self.b = b
        MyClass.count += 1
        #MyClass.instances.append(self)

    def __str__(self):
        return  self.__repr__()

    def __repr__(self):
        return "a: " + str(self.a) + ", b: " + str(self.b)

    def __del__(self):
        MyClass.count -= 1
        #MyClass.instances.remove(self)

A = MyClass(1,'abc')
B = MyClass(2,'def')
print MyClass.count
del B
print MyClass.count

有了评论,我得到了正确的答案:

2
1

但是没有评论——包括现在的静态对象列表 MyClass.instances 我得到了错误的答案:

2
2

似乎 MyClass 无法再使用它的__del__方法了!怎么来的?

4

4 回答 4

4

文档中,

del x doesn’t directly call x.__del__() — the former decrements the reference
count for x by one, and the latter is only called when x‘s reference count
reaches zero. 

当你取消注释时,

instances = []
...
...
MyClass.instances.append(self)

您正在将对当前对象的引用存储在MyClass.instances. 这意味着,引用计数在内部增加 1。这就是为什么__del__不立即调用的原因。

要解决此问题,请像这样从列表中明确删除该项目

MyClass.instances.remove(B)
del B

现在它将打印

2
1

正如预期的那样。

还有另一种方法可以解决此问题。也就是使用weakref. 从文档中,

对对象的弱引用不足以使对象保持活动状态:当对所指对象的唯一剩余引用是弱引用时,垃圾收集可以自由地销毁所指对象并将其内存用于其他用途。弱引用的主要用途是实现包含大对象的缓存或映射,其中不希望大对象仅仅因为它出现在缓存或映射中而保持活动状态。

因此,拥有 aweakref不会推迟对象的删除。,weakref这可以像这样修复

MyClass.instances.append(weakref.ref(self))
...
...
# MyClass.instances.remove(weakref.ref(self))
MyClass.instances = [w_ref for w_ref in MyClass.instances if w_ref() is None]

我们可以不使用remove方法,而是调用每个weakref对象,如果它们返回None,它们已经死了。所以,我们用列表推导过滤掉它们。

所以,现在,当你说s 时del B,即使weakrefs 存在B,它也会调用__del__(除非你已经让其他变量指向同一个对象,比如通过赋值)。

于 2014-01-15T10:49:05.060 回答
1

__del__仅在没有更多实例时调用。

您应该考虑仅将弱引用放入MyClass.instances列表中。

这可以通过import weakref然后

  • 要么使用WeakSet列表
  • 或放入weakref.ref(self)列表中。

__del__每当删除最后一个“严格”引用时,都会自动调用。弱引用自动消失。

但请注意,文档__del__中提到了一些注意事项。

于 2014-01-15T10:50:34.283 回答
1

从到http://docs.python.org/2.7/reference/datamodel.html#basic-customization我引用(后面的灰色段落object.__del__):

del x不直接调用x.__del__()——前者将 x 的引用计数减一,而后者仅在 x 的引用计数达到零时调用。

这里你调用del B了,但是 MyClass.instances 中仍然有一个 B 的实例,所以 B 仍然被引用,因此没有被销毁,所以__del__函数没有被调用。如果你直接打电话B.__del__(),它可以工作。

于 2014-01-15T10:48:52.723 回答
0

__del__当垃圾收集器从内存中删除一个对象时使用。如果您将对象添加到 MyClass.instances,则该对象将被标记为“已使用”,并且垃圾收集器将永远不会尝试将其删除。所以__del__永远不会被调用。

您最好使用显式函数 (MyClass.del_element()),因为您无法真正预测何时__del__会被调用(即使您没有将其添加到列表中)。

于 2014-01-15T10:54:27.590 回答