7

我是 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')
Initializing Swaroop
Swaroop 说再见
我是最后一个
Initializing Kalem
Kalem 说再见
我是最后一个

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

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

谢谢,埃里克

4

2 回答 2

19

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

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

希望这是有道理的。我明白为什么这可能会让学习 Python 的人感到困惑。Person4在重新定义类 using的同时使用类变量execfile()会进一步混淆问题。对于它的价值,我已经编写了很多 Python 代码,而且我认为我不需要使用__del__特殊方法。

于 2009-12-20T07:54:26.873 回答
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
于 2009-12-20T11:26:30.173 回答