Python 静态变量列表 __del__

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

文档中得知,

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。从文档中可以看到,

对一个对象进行弱引用并不足以使该对象保持存活状态:当指向某个对象的所有引用都是弱引用时,垃圾回收器可以自由地销毁该对象,并将其内存重新用于其他用途。弱引用的主要用途是实现缓存或映射,用于保存大型对象,因为希望大型对象不仅仅因为出现在缓存或映射中而被保留。

因此,拥有一个 weakref 并不能推迟对象的删除。通过使用 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]

我们可以调用每个weakref对象而不是使用remove方法,如果它们返回None,则表示它们已经失效。因此,我们可以使用列表推导式来过滤它们。

因此,现在,即使对于B存在weakref,当您说del B时,它也会调用__del__(除非您让其他变量指向同一个对象,例如通过赋值)。


1

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

del x并不直接调用x.__del__()——前者将x的引用计数减1,只有当x的引用计数达到零时才会调用后者。

在这里,您调用了del B,但是MyClass.instances中仍然有一个B的实例,因此B仍然被引用,因此__del__函数不会被调用。如果您直接调用B.__del__(),它会起作用。


1

__del__仅在没有实例剩余时才会被调用。

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

这可以通过import weakref实现,然后

  • 使用WeakSet来替代列表
  • 或者将weakref.ref(self)放入列表中。

__del__在最后一个“严格”引用被删除时会自动调用。弱引用会自动消失。

但请注意,在docs中提到了一些关于__del__的注意事项。


0

__del__ 在垃圾回收器从内存中移除对象时使用。如果您将对象添加到 MyClass.instances 中,则该对象将被标记为“已使用”,垃圾回收器将永远不会尝试将其删除。因此,__del__ 永远不会被调用。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接