在__del__中进行对象关闭/处置真的可以吗?
我最近在思考我在Python中写类的方式。更具体地说,就是构造函数是怎么实现的,以及对象应该如何被销毁。我不想依赖CPython的引用计数来清理对象。这基本上告诉我,我应该使用with语句来管理对象的生命周期,并且我需要一个明确的关闭或处理方法(这个方法可以在对象也是上下文管理器时,从__exit__
调用)。
class Foo(object):
def __init__(self):
pass
def close(self):
pass
现在,如果我所有的对象都是这样工作,并且我所有的代码都使用with语句或者明确调用close()
(或者dispose()
),那么我真的看不出在__del__
里放任何代码的必要性。我们真的应该用__del__
来处理我们的对象吗?
3 个回答
如果你确定不会出现循环引用,那么用 __del__
这样的方法是可以的:只要引用计数变为零,CPython 虚拟机就会调用这个方法并销毁对象。
如果你打算使用循环引用,建议你仔细考虑一下,并检查一下弱引用是否能帮上忙;在很多情况下,循环引用往往是设计不当的一个信号。
如果你无法控制对象的使用方式,那么使用 __del__
可能就不安全了。
如果你打算使用 JPython 或 IronPython,那么 __del__
就完全不可靠,因为对象的最终销毁是在垃圾回收时发生的,而这你是无法控制的。
总的来说,我认为 __del__
通常是安全且有效的;不过在很多情况下,退一步思考,从不同的角度看问题可能会更好;合理使用 try/except 和 with 语句可能是更符合 Python 风格的解决方案。
不一定会这样。当你有循环引用的时候,就会遇到问题。Eli Bendersky在他的博客中很好地解释了这个问题:
简短回答:不。
详细回答:使用 __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__
也没有被调用。