【问题标题】:del MyClass doesn't call object.__del__()del MyClass 不调用 object.__del__()
【发布时间】:2012-02-13 00:58:43
【问题描述】:

我有一个类可以打开一个文件进行写入。在我的析构函数中,我调用了关闭文件的函数:

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.

如果我在del myobj 之前调用myobj.close(),我不会遇到这个问题。那么为什么__del__() 没有被调用呢?

【问题讨论】:

  • 尝试从 object 继承 - 不这样做已被认为已过时六年。
  • 您应该显示更多代码。一个人怎么能告诉你myobj.close() 是如何改变事物的,却不知道它做了什么?
  • 请记住,del 不会调用 __del__。它只是删除了其中一个引用。通常最好显式关闭,可能使用上下文管理器协议(with 语句)。
  • @Marcin - 解决了它......作为答案发布?仍然好奇为什么会这样......

标签: python destructor


【解决方案1】:

您确定要使用__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:
    ...

当 Python 离开 with-block 时,将调用 myobj.__exit__(以及 self.__fileHandle__.close())。

【讨论】:

  • +1 用于提及问题。 docs.python.org/library/gc.html#gc.garbage 说 «具有 __del__() 方法并且是引用循环的一部分的对象会导致整个引用循环无法收集,包括不一定在循环中但只能从中访问的对象。»
  • 我看不到 OP 的示例中如何存在参考循环。有一个对象,它得到了del'd,而__del__() 没有被调用。
  • 另外,在我的情况下,我宁愿让 GC 处理东西,因为它不需要立即全部关闭,只是最终......但它总是有问题。不,上下文管理器不是真正的解决方案。它们限制您在同一个函数调用中关闭对象,并在每次使用时将代码缩进一级。当我想创建其中的 20 个时,它变得不可读。
【解决方案2】:

del 不是这样做的。不幸的是__del__del 同名,因为它们彼此没有关系。在现代术语中,__del__ 方法将被称为 finalizer,而不是 destructor,区别很重要。

简短的区别是很容易保证何时调用析构函数,但您几乎无法保证何时调用__del__,而且它可能永远不会被调用。有许多不同的情况会导致这种情况。

如果您想要词法作用域,请使用 with 语句。否则,请直接致电myobj.close()del 语句只删除引用,而不是对象。

我找到了另一个答案 (link),可以更详细地回答另一个问题。不幸的是,该问题的公认答案包含严重错误。

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

【讨论】:

    【解决方案3】:

    您的代码应继承自 object - 不这样做已被视为已过时(特殊情况除外)至少六年。

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

    为什么需要从 object 继承的简短版本是 __del__ 只是新式类的“魔法”。

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

    【讨论】:

    • 啊刚刚发现了一个实例,这个“魔法”不起作用……改用 ipython。
    • @tdc:除非我弄错了,否则 ipython 是基于 cpython 的。在任何情况下,如果您依赖特定实例的行为,您的代码将是不可移植的。使用其他答案中推荐的上下文管理器方法可能会更好。
    【解决方案4】:

    也许有别的东西在引用它,这就是为什么 __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/

    【讨论】:

    • 在这种情况下肯定只有一个参考
    猜你喜欢
    • 2011-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-25
    • 2011-11-06
    • 1970-01-01
    • 1970-01-01
    • 2017-04-08
    相关资源
    最近更新 更多