【发布时间】:2010-12-01 16:13:17
【问题描述】:
我看到一个定义了__del__ method 的类。此方法用于销毁类的实例。但是,我找不到使用这种方法的地方。这种方法是如何使用的?像这样:obj1.del()?.
如何调用__del__ 方法?
【问题讨论】:
我看到一个定义了__del__ method 的类。此方法用于销毁类的实例。但是,我找不到使用这种方法的地方。这种方法是如何使用的?像这样:obj1.del()?.
如何调用__del__ 方法?
【问题讨论】:
__del__ 是一个终结器。当一个对象被垃圾收集时调用它,这发生在对该对象的所有引用都被删除之后的某个时刻。
在简单情况中,这可能是在您说del x 之后,或者如果x 是一个局部变量,则在函数结束之后。特别是,除非存在循环引用,否则 CPython(标准 Python 实现)将立即进行垃圾收集。
不过,这是 CPython 的实现细节。 Python 垃圾回收的唯一必需属性是它发生在所有引用都被删除之后,所以这可能没有必要在之后发生并且可能根本不会发生。
更重要的是,由于许多原因,变量可以长期存在,例如传播异常或模块自省可以使变量引用计数保持大于 0。此外,变量可以是引用循环的一部分——打开垃圾收集的 CPython 会中断大多数(但不是全部)这样的循环,即便如此,也只是周期性的。
既然你不能保证它会被执行,那么你应该永远不要将你需要运行的代码放入__del__()——相反,这段代码属于try的finally子句在with 语句中阻止或发送到上下文管理器。但是,__del__ 有有效用例:例如如果对象X 引用Y 并且还在全局cache (cache['X -> Y'] = Y) 中保留Y 引用的副本,那么X.__del__ 也删除缓存条目是礼貌的。
如果您知道析构函数提供了(违反上述准则)所需的清理,您可能希望直接调用它,因为没有什么特别之处它作为一种方法:x.__del__()。显然,只有当你知道它可以被调用两次时,你才应该这样做。或者,作为最后的手段,您可以使用重新定义此方法
type(x).__del__ = my_safe_cleanup_method
【讨论】:
__del__ 方法即使在程序终止时也可能不会运行,即使它们确实在终止时运行,编写一个 __del__ 方法即使在解释器忙于自毁时也能正常工作你身边的人需要比许多程序员更仔细的编码。 (CPython 清理通常会在解释器关闭时运行 __del__ 方法,但仍然存在不够的情况。守护线程、C 级全局变量和在另一个 __del__ 中创建的 __del__ 对象都可以导致到__del__ 方法未运行。)
__init__ 不是构造函数相同。没有人实际创建或销毁对象。 __init__ 方法初始化一个在被调用时已经存在的对象(这就是为什么它也不返回任何东西的原因)。同样,在__del__ 退出时对象仍然存在:它只是在实际销毁发生之前进行任何可能需要的清理(“终结”)。
如前所述,__del__ 功能有些不可靠。如果它看起来有用,请考虑改用__enter__ 和__exit__ 方法。这将提供类似于用于访问文件的with open() as f: pass 语法的行为。进入with范围时自动调用__enter__,退出时自动调用__exit__。详情请见this question。
【讨论】:
我写了另一个问题的答案,尽管这是一个更准确的问题。
How do constructors and destructors work?
这是一个有点自以为是的答案。
不要使用__del__。这不是 C++ 或为析构函数构建的语言。 __del__ 方法确实应该在 Python 3.x 中消失,尽管我相信有人会找到一个有意义的用例。如果您需要使用__del__,请注意http://docs.python.org/reference/datamodel.html 的基本限制:
__del__ 在垃圾收集器碰巧收集对象时调用,而不是在您丢失对对象的最后一个引用时调用,而不是在您执行 del object 时调用。__del__ 负责调用超类中的任何 __del__,但尚不清楚这是按方法解析顺序 (MRO) 还是仅调用每个超类。__del__ 意味着垃圾收集器放弃检测和清理任何循环链接,例如丢失对链接列表的最后引用。您可以从 gc.garbage 中获取忽略的对象列表。您有时可以使用弱引用来完全避免循环。时不时会对此进行辩论:请参阅http://mail.python.org/pipermail/python-ideas/2009-October/006194.html。__del__ 函数可以作弊,保存对对象的引用并停止垃圾回收。__del__ 中显式引发的异常将被忽略。__del__ 补充 __new__ 远远超过 __init__。这变得令人困惑。请参阅 http://www.algorithm.co.il/blogs/programming/python-gotchas-1-del-is-not-the-opposite-of-init/ 了解解释和问题。__del__ 不是 Python 中“深受喜爱”的孩子。您会注意到 sys.exit() 文档没有指定是否在退出之前收集垃圾,并且存在许多奇怪的问题。在全局变量上调用 __del__ 会导致奇怪的排序问题,例如 http://bugs.python.org/issue5099。即使__init__ 失败,是否应该调用__del__?请参阅http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423 了解长线程。但是,另一方面:
__del__ 表示您不要忘记调用 close 语句。请参阅http://eli.thegreenplace.net/2009/06/12/safely-using-destructors-in-python/ 了解专业人士__del__ 的观点。这通常是关于释放 ctypes 或其他一些特殊资源。还有我不喜欢__del__ 功能的个人原因。
__del__ 时,都会演变成 30 条令人困惑的消息。所以,找个理由不使用__del__。
【讨论】:
__del__,而是如何调用__del__,你的回答很有趣。
__del__ 方法,在对象被垃圾回收时调用。请注意,它不一定会被调用。以下代码本身不一定会这样做:
del obj
原因是del 只是将引用计数减一。如果其他东西引用了该对象,__del__ 将不会被调用。
不过,使用__del__ 有一些注意事项。通常,它们通常不是很有用。在我看来,您更像是想使用 close 方法或者with statement。
请参阅python documentation on __del__ methods。
还有一点需要注意:__del__ 方法如果过度使用会抑制垃圾收集。特别是,具有多个具有__del__ 方法的对象的循环引用不会被垃圾回收。这是因为垃圾收集器不知道先调用哪个。有关详细信息,请参阅gc module 上的文档。
【讨论】:
当你的对象最终被销毁时,会调用__del__ 方法(注意拼写!)。从技术上讲(在 cPython 中),即没有更多对您的对象的引用,即当它超出范围时。
如果你想删除你的对象并调用 __del__ 方法使用
del obj1
这将删除对象(前提是没有任何其他对它的引用)。
我建议你写一个这样的小班
class T:
def __del__(self):
print "deleted"
并在 python 解释器中进行调查,例如
>>> a = T()
>>> del a
deleted
>>> a = T()
>>> b = a
>>> del b
>>> del a
deleted
>>> def fn():
... a = T()
... print "exiting fn"
...
>>> fn()
exiting fn
deleted
>>>
请注意,jython 和 ironpython 对于删除对象和调用 __del__ 的确切时间有不同的规则。使用__del__ 被认为不是一种好的做法,但由于这个原因以及对象及其环境在被调用时可能处于未知状态这一事实。也不能绝对保证 __del__ 会被调用 - 解释器可以以各种方式退出而不删除所有对象。
【讨论】:
use del obj1 似乎是个糟糕的主意。