在__del__中进行对象关闭/处置真的可以吗?

8 投票
3 回答
8201 浏览
提问于 2025-04-15 12:49

我最近在思考我在Python中写类的方式。更具体地说,就是构造函数是怎么实现的,以及对象应该如何被销毁。我不想依赖CPython的引用计数来清理对象。这基本上告诉我,我应该使用with语句来管理对象的生命周期,并且我需要一个明确的关闭或处理方法(这个方法可以在对象也是上下文管理器时,从__exit__调用)。

class Foo(object):
    def __init__(self):
        pass
    def close(self):
        pass

现在,如果我所有的对象都是这样工作,并且我所有的代码都使用with语句或者明确调用close()(或者dispose()),那么我真的看不出在__del__里放任何代码的必要性。我们真的应该用__del__来处理我们的对象吗?

3 个回答

0

如果你确定不会出现循环引用,那么用 __del__ 这样的方法是可以的:只要引用计数变为零,CPython 虚拟机就会调用这个方法并销毁对象。

如果你打算使用循环引用,建议你仔细考虑一下,并检查一下弱引用是否能帮上忙;在很多情况下,循环引用往往是设计不当的一个信号。

如果你无法控制对象的使用方式,那么使用 __del__ 可能就不安全了。

如果你打算使用 JPython 或 IronPython,那么 __del__ 就完全不可靠,因为对象的最终销毁是在垃圾回收时发生的,而这你是无法控制的。

总的来说,我认为 __del__ 通常是安全且有效的;不过在很多情况下,退一步思考,从不同的角度看问题可能会更好;合理使用 try/except 和 with 语句可能是更符合 Python 风格的解决方案。

8

不一定会这样。当你有循环引用的时候,就会遇到问题。Eli Bendersky在他的博客中很好地解释了这个问题:

15

简短回答:不。

详细回答:使用 __del__ 是有点棘手的,主要是因为它不一定会被调用。这意味着你不能在这里做一些绝对必须完成的事情。这也就意味着 __del__ 只能用来处理那些反正迟早会发生的清理工作,比如清理一些在程序退出时会被清理的资源,所以如果 __del__ 没有被调用也没关系。当然,这些通常也是 Python 会自动帮你处理的事情。所以这就让 __del__ 基本上变得没什么用。

另外,__del__ 会在 Python 垃圾回收的时候被调用,而你并不想等到 Python 垃圾回收,这意味着你根本不能使用 __del__

所以,不要使用 __del__。用 __enter__/__exit__ 来代替。

顺便说一下:这里有一个非循环的例子,说明析构函数没有被调用:

class A(object):
    def __init__(self):
        print('Constructing A')

    def __del__(self):
        print('Destructing A')

class B(object):
    a = A()

好吧,这里是一个类属性。显然这是一个特殊情况。但这说明确保 __del__ 被调用并不是那么简单。我很确定我见过更多非循环的情况,__del__ 也没有被调用。

撰写回答