什么是__del__方法,如何调用?

问题:什么是__del__方法,如何调用?

我正在阅读代码。有一个__del__定义方法的类。我发现此方法用于销毁该类的实例。但是,我找不到使用此方法的地方。这样做的主要原因是我不知道如何使用此方法,可能不是这样:obj1.del()。那么,我的问题是如何调用该__del__方法?

I am reading a code. There is a class in which __del__ method is defined. I figured out that this method is used to destroy an instance of the class. However, I cannot find a place where this method is used. The main reason for that is that I do not know how this method is used, probably not like that: obj1.del(). So, my questions is how to call the __del__ method?


回答 0

__del__终结器。当删除对象的所有引用之后的某个时刻发生垃圾回收时,调用该方法。

在一个简单的例子中,这可能是在您说完之后,del x或者如果x是局部变量,则在函数结束之后。特别是,除非有循环引用,否则CPython(标准Python实现)将立即进行垃圾回收。

但是,这是CPython 的实现细节。Python垃圾回收的唯一必需属性是,它会删除所有引用之后发生,因此这可能之后没有必要发生可能根本没有发生

此外,由于多种原因,变量可以生存很长一段时间,例如,传播异常或模块自省可以使变量引用计数保持大于0。此外,变量可以是引用循环的一部分-启用垃圾回收的CPython多数会中断,但不是全部,这样的周期,甚至只是周期性的。

由于您无法保证它会被执行,因此永远不要将您需要运行的代码放入其中__del__()-而是,该代码属于块的finally子句trywith语句中的上下文管理器。不过,也有有效的使用情况__del__:例如,如果一个对象X的引用Y,也保留副本Y参考在全球cachecache['X -> Y'] = Y),那么这将是一个有礼貌X.__del__也删除缓存条目。

如果您知道析构函数提供了必要的清除操作(违反了上述准则),则您可能希望直接调用它,因为该方法没有什么特别之处:x.__del__()。显然,仅当您知道不介意被两次调用时,才应该这样做。或者,作为最后的选择,您可以使用重新定义此方法

type(x).__del__ = my_safe_cleanup_method  

__del__ is a finalizer. It is called when an object is garbage collected which happens at some point after all references to the object have been deleted.

In a simple case this could be right after you say del x or, if x is a local variable, after the function ends. In particular, unless there are circular references, CPython (the standard Python implementation) will garbage collect immediately.

However, this is an implementation detail of CPython. The only required property of Python garbage collection is that it happens after all references have been deleted, so this might not necessary happen right after and might not happen at all.

Even more, variables can live for a long time for many reasons, e.g. a propagating exception or module introspection can keep variable reference count greater than 0. Also, variable can be a part of cycle of references — CPython with garbage collection turned on breaks most, but not all, such cycles, and even then only periodically.

Since you have no guarantee it’s executed, one should never put the code that you need to be run into __del__() — instead, this code belongs to finally clause of the try block or to a context manager in a with statement. However, there are valid use cases for __del__: e.g. if an object X references Y and also keeps a copy of Y reference in a global cache (cache['X -> Y'] = Y) then it would be polite for X.__del__ to also delete the cache entry.

If you know that the destructor provides (in violation of the above guideline) a required cleanup, you might want to call it directly, since there is nothing special about it as a method: x.__del__(). Obviously, you should you do so only if you know that it doesn’t mind to be called twice. Or, as a last resort, you can redefine this method using

type(x).__del__ = my_safe_cleanup_method  

回答 1

我写下了另一个问题的答案,尽管这是一个更准确的问题。

构造函数和析构函数如何工作?

这是一个有点自以为是的答案。

不要使用__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- 德尔 -is-不是最相反OF- 的init /一个解释和陷阱。
  • __del__在Python中不是一个“受欢迎的”孩子。您会注意到sys.exit()文档没有指定是否在退出之前收集垃圾,并且存在很多奇怪的问题。调用__del__on全局变量会导致奇怪的排序问题,例如http://bugs.python.org/issue5099__del__即使__init__失败也应该打电话吗?有关长线程的信息,请参见http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423

但另一方面:

我不喜欢该__del__功能的原因。

  • 每当有人提出来时,__del__它就会演变成三十种混乱的信息。
  • 它在Python的Zen中打破了这些项目:
    • 简单胜于复杂。
    • 特殊情况不足以违反规则。
    • 错误绝不能默默传递。
    • 面对模棱两可的想法,拒绝猜测的诱惑。
    • 应该有一种(最好只有一种)明显的方式来做到这一点。
    • 如果实现难以解释,那是个坏主意。

因此,找到不使用的理由__del__

I wrote up the answer for another question, though this is a more accurate question for it.

How do constructors and destructors work?

Here is a slightly opinionated answer.

Don’t use __del__. This is not C++ or a language built for destructors. The __del__ method really should be gone in Python 3.x, though I’m sure someone will find a use case that makes sense. If you need to use __del__, be aware of the basic limitations per http://docs.python.org/reference/datamodel.html:

  • __del__ is called when the garbage collector happens to be collecting the objects, not when you lose the last reference to an object and not when you execute del object.
  • __del__ is responsible for calling any __del__ in a superclass, though it is not clear if this is in method resolution order (MRO) or just calling each superclass.
  • Having a __del__ means that the garbage collector gives up on detecting and cleaning any cyclic links, such as losing the last reference to a linked list. You can get a list of the objects ignored from gc.garbage. You can sometimes use weak references to avoid the cycle altogether. This gets debated now and then: see http://mail.python.org/pipermail/python-ideas/2009-October/006194.html.
  • The __del__ function can cheat, saving a reference to an object, and stopping the garbage collection.
  • Exceptions explicitly raised in __del__ are ignored.
  • __del__ complements __new__ far more than __init__. This gets confusing. See http://www.algorithm.co.il/blogs/programming/python-gotchas-1-del-is-not-the-opposite-of-init/ for an explanation and gotchas.
  • __del__ is not a “well-loved” child in Python. You will notice that sys.exit() documentation does not specify if garbage is collected before exiting, and there are lots of odd issues. Calling the __del__ on globals causes odd ordering issues, e.g., http://bugs.python.org/issue5099. Should __del__ called even if the __init__ fails? See http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423 for a long thread.

But, on the other hand:

And my pesonal reason for not liking the __del__ function.

  • Everytime someone brings up __del__ it devolves into thirty messages of confusion.
  • It breaks these items in the Zen of Python:
    • Simple is better than complicated.
    • Special cases aren’t special enough to break the rules.
    • Errors should never pass silently.
    • In the face of ambiguity, refuse the temptation to guess.
    • There should be one – and preferably only one – obvious way to do it.
    • If the implementation is hard to explain, it’s a bad idea.

So, find a reason not to use __del__.


回答 2

__del__方法将在垃圾回收对象时被调用。请注意,不一定要调用它。以下代码本身不一定会这样做:

del obj

原因是del只是将引用计数减一。如果还有其他对象引用,__del__则不会调用。

__del__尽管有一些注意事项。通常,它们通常不是很有用。在我看来,这更像是您要使用close方法或with语句

请参阅有关__del__方法python文档

需要注意的另一件事: __del__如果使用过多的方法,可能会阻止垃圾回收。特别是,使用一种__del__方法具有多个对象的循环引用将不会收集垃圾。这是因为垃圾收集器不知道首先调用哪个。有关更多信息,请参见gc模块上的文档。

The __del__ method, it will be called when the object is garbage collected. Note that it isn’t necessarily guaranteed to be called though. The following code by itself won’t necessarily do it:

del obj

The reason being that del just decrements the reference count by one. If something else has a reference to the object, __del__ won’t get called.

There are a few caveats to using __del__ though. Generally, they usually just aren’t very useful. It sounds to me more like you want to use a close method or maybe a with statement.

See the python documentation on __del__ methods.

One other thing to note: __del__ methods can inhibit garbage collection if overused. In particular, a circular reference that has more than one object with a __del__ method won’t get garbage collected. This is because the garbage collector doesn’t know which one to call first. See the documentation on the gc module for more info.


回答 3

__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
>>>   

请注意,关于删除和__del__调用对象的确切时间,jython和ironpython具有不同的规则。__del__由于这个原因以及调用对象和其环境可能处于未知状态这一事实,因此不认为使用该方法是一种好习惯。也不绝对保证__del__会被调用-解释器可以以各种方式退出而不删除所有对象。

The __del__ method (note spelling!) is called when your object is finally destroyed. Technically speaking (in cPython) that is when there are no more references to your object, ie when it goes out of scope.

If you want to delete your object and thus call the __del__ method use

del obj1

which will delete the object (provided there weren’t any other references to it).

I suggest you write a small class like this

class T:
    def __del__(self):
        print "deleted"

And investigate in the python interpreter, eg

>>> 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
>>>   

Note that jython and ironpython have different rules as to exactly when the object is deleted and __del__ is called. It isn’t considered good practice to use __del__ though because of this and the fact that the object and its environment may be in an unknown state when it is called. It isn’t absolutely guaranteed __del__ will be called either – the interpreter can exit in various ways without deleteting all objects.


回答 4

如前所述,该__del__功能有些不可靠。在看起来有用的情况下,请考虑使用__enter__and __exit__方法。这将产生类似于with open() as f: pass用于访问文件的语法的行为。__enter__进入时with__exit__会自动调用,退出时会自动调用。有关更多详细信息,请参见此问题

As mentioned earlier, the __del__ functionality is somewhat unreliable. In cases where it might seem useful, consider using the __enter__ and __exit__ methods instead. This will give a behaviour similar to the with open() as f: pass syntax used for accessing files. __enter__ is automatically called when entering the scope of with, while __exit__ is automatically called when exiting it. See this question for more details.