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

ELe*_*Lee 6 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')
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

谢谢,埃里克

Gre*_*ill 19

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

现在,swaroopkaleem全局变量,无论是你的"test1.py"的实例之间共享.Python内部使用引用计数来进行大多数自动垃圾收集,因此第一个Person4类的原始实例在第二次赋值之前不会被释放swaroop.分配swaroop减少第一个实例的引用计数,导致__del__被调用,因为引用计数现在为零.但是因为你在Person4里面用名字引用__del__(),当前一个实例消失时,它会减少新的 Person4.population计数,而不是旧的Person4人口数.

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


Ala*_*oni 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)
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

  • 更好的是,你应该记住,Python在静态类型语言的意义上没有"变量"的概念.只有对象和名称绑定这些对象 - 一旦一个对象没有被任何东西引用,它就会被销毁 - 但是没有"内存位置",当你改变它的值时,它的值会被覆盖 - "="运算符__rebinds__而是改为使用不同引用的名称. (2认同)