为什么不能保证在解释器退出时调用析构函数?

She*_*evy 14 python destructor

python文档:

无法保证__del__()在解释器退出时仍然存在的对象调用方法.

为什么不?如果做出这种保证会出现什么问题?

str*_*bly 7

我不相信以前的答案.

首先请注意,给出的示例不会阻止__del__在退出期间调用方法.事实上,当前的CPythons 调用__del__给定的方法,在Python 2.7的情况下调用两次,在Python 3.4的情况下调用一次.所以这不能成为"杀手级的例子",它说明了为什么不做出保证.

我认为文档中的陈述不是出于设计原则的动机,即调用析构函数会很糟糕.尤其是因为似乎在CPython 3.4及更高版本中它们总是按照您的预期被调用,这个警告似乎没有实际意义.

相反,我认为该语句只是反映了这样一个事实:CPython实现有时并没有在退出时调用所有析构函数(大概是为了便于实现).

情况似乎是CPython 3.4和3.5总是在解释器退出时调用所有析构函数.

相比之下,CPython 2.7并不总能做到这一点.当然__del__,通常不会在具有循环引用的对象上调用方法,因为如果这些对象具有__del__方法,则无法删除这些对象.垃圾收集器不会收集它们.当解释器退出时,对象确实消失了(当然)它们没有最终确定,因此它们的__del__方法永远不会被调用.在实施PEP 442之后,Python 3.4中不再适用.

但是,似乎Python 2.7也没有最终确定具有循环引用的对象,即使它们没有析构函数,如果它们只在解释器出口期间变得无法访问.

据推测,这种行为是特别的,很难解释它最好只是通过一般免责声明表达 - 正如文档所做的那样.

这是一个例子:

class Foo(object):
    def __init__(self):
        print("Foo init running")

    def __del__(self):
        print("Destructor Foo")

class Bar(object):
    def __init__(self):
        print("Bar1 init running")
        self.bar = self
        self.foo = Foo()

b = Bar()

# del b
Run Code Online (Sandbox Code Playgroud)

随着del b注释掉,在析构函数Foo是不是在Python 2.7叫虽然它是在Python 3.4.

随着del b增加,析构函数是在两种情况下调用(在解释退出).

  • 这个答案是对的.Python 3.4+仍然无法保证将调用析构函数.[这里](https://ideone.com/2UvmrW)一个基于sebix在页面上回答的简单示例.Python 3.4+尝试比早期版本更难,但它仍然只是尽力而为的基础. (2认同)

mgi*_*son 5

如果你做了一些令人讨厌的事情,你可能会发现自己有一个不可删除的对象,python 会尝试永远删除该对象:

class Phoenix(object):
    def __del__(self):
        print "Deleting an Oops"
        global a
        a = self

a = Phoenix()
Run Code Online (Sandbox Code Playgroud)

__del__无论如何,依赖都不是很好,因为 python 不保证何时删除对象(尤其是具有循环引用的对象)。也就是说,也许将您的类变成上下文管理器是一个更好的解决方案...然后您可以保证即使在发生异常等情况下也会调用清理代码...

  • @SheaLevy——重点是Python提供了一个优秀的工具来自动处理清理(查看上下文管理器)。这只是一种更安全的处理方式。 (2认同)