我是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__
?
在重用之前检查现有实例名称?
...
谢谢, 埃里克
答案 0 :(得分:19)
这里有几件事情要发生。当您的Person4
类被实例化时,它会将其population
类变量初始化为0.在交互式控制台中,您似乎多次运行“test1.py”文件。第二次运行它时,再次声明Person4
类,使其在技术上与第一个不同(即使它具有相同的名称)。这意味着它有自己独立的population
计数。
现在,swaroop
和kaleem
是全局变量,在“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