2014-01-15 46 views
3

我正在尝试使用静态列表来创建一个类,它收集对象类的所有新实例。我面临的问题,似乎只要我尝试像使用整数一样使用列表,我不能再使用神奇标记__del__Python静态变量列表__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__方法a nymore!怎么来的?

回答

1

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

del x不直接调用x.__del__() - 前递减x的引用计数的一个,而后者只调用当x参考计数达到零。

在这里,您拨打del B但仍有B的一个实例在MyClass.instances,所以B是仍然被引用,因此不被破坏,从而使__del__函数没有被调用。 如果你直接打电话B.__del__(),它的工作原理。

3

docs

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。从docs

弱引用一个对象是不够的,以保持物体活着: 时,所指对象仅存的引用是弱引用, 垃圾回收是免费摧毁指涉和重用其 记忆别的东西。弱引用的主要用途是 实现缓存或持有大对象的映射,其中期望 大对象不会因仅出现在 缓存或映射中而保持活动状态。

因此,拥有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] 

而不是使用remove的方法,我们可以调用每个weakref对象,如果他们返回None,他们已经死了。所以,我们用列表理解过滤出来。

所以,现在,当你说del B,即使对于B存在weakref S,它会调用__del__(除非你已经取得了一些其他变量指向同一个对象,就像做一个分配新建分配FY)。

1

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

您应该考虑在MyClass.instances列表中只放入弱符号。

这可以用import weakref然后

  • 或者使用一个WeakSet为列表
  • 或将weakref.ref(self)到列表中来实现和。

__del__每当最后一个“严格”引用被删除时自动调用。这些虚假信息会自动消失。

但请注意,docs中提到的__del__有一些注意事项。

0

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

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