2012-01-18 74 views
6

我有一个打开文件进行写入的类。在我的析构函数,我调用关闭该文件的函数:del MyClass不会调用object .__ del __()

class MyClass: 
    def __del__(self): 
     self.close() 

    def close(self): 
     if self.__fileHandle__ is not None: 
       self.__fileHandle__.close() 

但是当我喜欢的代码删除对象:

myobj = MyClass() 
myobj.open() 
del myobj 

,如果我尝试重新实例化对象,我得到一个数值错误:

ValueError: The file 'filename' is already opened. Please close it before reopening in write mode. 

,而如果我叫myobj.close()del myobj之前,我不明白这个问题。那么为什么__del__()没有被叫?

+1

尝试从'对象'继承 - 不这样做已经被认为是过时的风格约六年。 – Marcin 2012-01-18 09:37:10

+0

你应该展示更多的代码。怎么能告诉你'myobj.close()'如何改变,而不知道它是干什么的? – ugoren 2012-01-18 09:37:49

+1

请记住'del'不会调用__del__'。它只是删除了其中一个参考。通常最好是明确关闭,可能使用上下文管理器协议('with'语句)。 – yak 2012-01-18 09:45:50

回答

8

您确定要使用__del__?有issues__del__和垃圾收集。

你可以做MyClass的一个context manager代替:

class MyClass(object): 
    def __enter__(self): 
     return self 
    def __exit__(self,ext_type,exc_value,traceback): 
     if self.__fileHandle__ is not None: 
       self.__fileHandle__.close() 

通过这样做,你可以使用MyClass这样的:

with MyClass() as myobj: 
    ... 

myobj.__exit__(因此self.__fileHandle__.close())将会被调用时Python的叶子with-block

+0

+1提到的问题。 http://docs.python.org/library/gc.html#gc.garbage表示:«具有__del __()方法并且是引用循环的一部分的对象会导致整个引用循环无法收集,包括不一定在该周期,但只能从它到达» – 2012-01-18 10:22:48

+0

我没有看到在OP的例子中有一个参考周期。有一个对象,它得到'del''d,'__del __()'不会被调用。 – sudo 2017-05-26 17:59:01

+1

另外,在我的情况下,我宁愿让GC处理一些东西,因为它不需要立即关闭,只是最终......但它总是有问题。不,环境管理者不是真正的解决方案。它们限制你在同一个函数调用中关闭对象,并在每次使用一个级别时将你的代码缩进一级。当我想创建其中的20个时,使其无法读取。 – sudo 2017-05-26 18:02:04

8

这不是del所做的。不幸的是,__del__del具有相同的名称,因为它们彼此不相关。在现代术语中,__del__方法将被称为终结器,而不是析构器并且区别是重要的。

简短的区别是,它很容易保证当调用析构函数,但你什么时候__del__将被称为极少数的保证,它可能永远不会叫。有很多不同的情况可能导致这种情况。

如果您想要词法范围确定,请使用with语句。否则,请直接拨打myobj.close()。语句del只删除引用,而不是对象。

我找到了另一个答案(link)到一个不同的问题,更详细地回答这个问题。不幸的是,对这个问题的接受答案含有极其严重的错误。

编辑:正如评论者指出,您需要继承object。这很好,但仍然有可能__del__永远不会被调用(你可能会很幸运)。请参阅上面的链接答案。

2

您的代码应该从object继承 - 如果不这样做,则至少在六年内已被视为过期(特殊情况除外)。

你可以阅读__del__这里:http://docs.python.org/reference/datamodel.html#object.del

为什么你需要从object继承简短的版本是,__del__仅适用于新的样式类的“神奇”。

如果您需要依赖调用终结器,我强烈建议您使用其他答案中推荐的上下文管理器方法,因为这是一种便携式,强大的解决方案。

+1

刚刚发现一个实例,这个“魔法”不起作用...使用ipython来代替。 – tdc 2012-01-18 10:21:51

+0

@tdc:除非我误解了,ipython是基于cpython的。无论如何,如果您依赖特定实例的行为,您的代码将不可移植。使用其他答案中推荐的上下文管理器方法可能会更好。 – Marcin 2012-01-18 10:46:20

0

也许别的东西引用它,这就是为什么__del__没有被调用(还)。

考虑以下代码:

#!/usr/bin/env python 

import time 

class NiceClass(): 
    def __init__(self, num): 
     print "Make %i" % num 
     self.num = num 
    def __del__(self): 
     print "Unmake %i" % self.num 

x = NiceClass(1) 
y = NiceClass(2) 
z = NiceClass(3) 
lst = [x, y, z] 
time.sleep(1) 
del x 
del y 
del z 
time.sleep(1) 
print "Deleting the list." 
del lst 
time.sleep(1) 

它不叫NiceClass__del__情况下,直到我们删除引用它们的名单。

与C++不同,__del__未被无条件地调用来按需破坏对象。 GC让事情变得更加困难。这里有一些信息:http://arctrix.com/nas/python/gc/

+0

在这种情况下肯定只有一个参考 – tdc 2012-01-18 10:07:18