当不期望时,在python中调用__del__方法

时间:2009-12-20 07:42:46

标签: python object-lifetime del

我是python的新手,并且一直在研究Swaroop CH的“A Byte of Python”中的示例。我看到一些令我困惑的__del__方法的行为。

基本上,如果我运行以下脚本(在Python 2.6.2中)

class Person4:
    '''Represents a person'''
    population = 0

    def __init__(self, name):
        '''Initialize the person's data'''
        self.name = name
        print 'Initializing %s'% self.name

        #When the person is created they increase the population
        Person4.population += 1

    def __del__(self):
        '''I am dying'''
        print '%s says bye' % self.name

        Person4.population -= 1

        if Person4.population == 0:
            print 'I am the last one'
        else:
            print 'There are still %d left' % Person4.population


swaroop = Person4('Swaroop')
kaleem = Person4('Kalem')

使用Python控制台(或Spyder交互式控制台),我看到以下内容:

  
    
      

的execfile(u'C:\ 1_eric \的Python \ test1.py')
      初始化Swaroop
      初始化Kalem

             

的execfile(u'C:\ 1_eric \的Python \ test1.py')
      初始化Swaroop
      Swaroop说再见       我是最后一个       初始化Kalem
      卡勒姆说再见       我是最后一个

    
  

为什么在第二次运行__del__之后立即调用__init__方法? 我猜测,因为正在使用相同的实例名称('swaroop'和'kaleem')它正在释放原始实例并且垃圾收集它。但是,这似乎对目前的人口数量造成了严重破坏。

这里发生了什么? 什么是避免这种混淆的好方法?
   避免使用__del__?    在重用之前检查现有实例名称?    ...

谢谢, 埃里克

2 个答案:

答案 0 :(得分:19)

这里有几件事情要发生。当您的Person4类被实例化时,它会将其population类变量初始化为0.在交互式控制台中,您似乎多次运行“test1.py”文件。第二次运行它时,再次声明Person4类,使其在技术上与第一个不同(即使它具有相同的名称)。这意味着它有自己独立的population计数。

现在,swaroopkaleem全局变量,在“test1.py”的两个实例之间共享。 Python内部对其大多数自动垃圾收集使用引用计数,因此第一个Person4类的原始实例在第二次分配给swaroop之前不会被释放。分配给swaroop会减少第一个实例的引用计数,导致__del__被调用,因为引用计数现在为零。但是因为您在Person4中按名称引用__del__(),当上一个实例消失时,它会减少 new Person4.population次数,而不是旧的Person4人口数。

希望这是有道理的。我可以看到为什么这可能让学习Python的人感到困惑。在使用Person4重新定义execfile()类的同时使用类变量进一步令人困惑。为了它的价值,我编写了很多Python代码,我认为我不需要使用__del__特殊方法。

答案 1 :(得分:7)

一般建议:不要在Python中使用__ del __。它可以通过多种方式破坏垃圾收集,尤其是在对象之间循环引用的情况下。

在您的示例中,存在与execfile()的使用相关的各种问题 - 这不是最佳实践 - 以及全局变量的重新定义。顺便说一句,如果你真的需要创建一个伪析构函数(即每当对象被垃圾收集时调用的代码),写一个所谓的“终结器”函数(它不是一个正确的析构函数)并使用weakref调用它.ref回调。它当然不应该是一个实例方法,并且记住lambda实际上创建了一个闭包,因此请确保不要在回调中泄漏对self的任何引用!如果你需要来自被破坏实例的数据,请使用func默认参数方法,只需确保从不在lambda中引用'self',否则它将无效。

from weakref import ref
from time import sleep

class Person4:
    '''Represents a person'''
    population = 0

    def __init__(self, name):
        '''Initialize the person's data'''
        self.name = name
        print 'Initializing %s'% self.name

        #When the person is created they increase the population
        Person4.population += 1

        self._wr = ref(self, lambda wr, name=self.name: Person4_finalizer(name))

def Person4_finalizer(name):
        '''I am dying'''
        print '%s says bye' % name

        Person4.population -= 1

        if Person4.population == 0:
            print 'I am the last one'
        else:
            print 'There are still %d left' % Person4.population

p1 = Person4("one")
p2 = Person4("two")
p3 = Person4("three")

del p2
del p3
sleep(5)

输出(睡眠是为了帮助看看发生了什么):

Initializing one
Initializing two
Initializing three
two says bye
There are still 2 left
three says bye
There are still 1 left
one says bye
I am the last one