Search code examples
pythondestructorfinalizer

What's the use of the __del__() method in Python?


From Python documentation:

It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits.

As far as I understand, there is also no way to guarantee an object stops existing before the interpreter exits, since it's up to the garbage collector to decide if and when an object is deleted.

So what's the point of having this method at all? You can write cleanup code inside it, but there's no guarantee it will ever be executed.

I know you can solve this using try-finally or with clauses, but I still wonder what would be a meaningful use case of the __del__() method.


Solution

  • After reading all of these answers—none of which satisfactorily answered all of my questions/doubts—and rereading Python documentation, I've come to a conclusion of my own. This the summary of my thoughts on the matter.


    Implementation-agnostic

    The passage you quoted from the __del__ method documentation says:

    It is not guaranteed that the __del__() methods are called for objects that still exist when the interpreter exits.

    But not only is it not guaranteed that __del__() is called for objects being destroyed during interpreter exit, it is not even guaranteed that objects are garbage collected at all, even during normal execution—from the "Data model" section of the Python Language Reference:

    Objects are never explicitly destroyed; however, when they become unreachable they may be garbage-collected. An implementation is allowed to postpone garbage collection or omit it altogether — it is a matter of implementation quality how garbage collection is implemented, as long as no objects are collected that are still reachable.

    Thus, replying to your question:

    So what's the point of having this method at all? You can write cleanup code inside it, but there's no guarantee it will ever be executed.

    From an implementation-agnostic perspective, are there any uses for the __del__ method, as a fundamental component of one's code that can be relied on? No. None at all. It is essentially useless from this perspective.

    From a practical point of view, though, as other answers have pointed out, you can use __del__ as a last-resort mechanism to (try to) ensure that any necessary cleanup is performed before the object is destroyed, e.g. releasing resources, if the user forgot to explicitly call a close method. This is not so much a fail-safe as it is a "it doesn't hurt to add an extra safety mechanism even if it's not guaranteed to work"—and in fact, most Python implementations will catch that most of the time. But it's nothing to be relied on.


    Implementation-specific

    That being said, if you know that your program will run on a specific set of Python implementations, then you can rely on the implementation details of garbage collection—for instance, if you use CPython, you can "rely on" the fact that, during normal execution (i.e. outside of interpreter exit), if the reference count of a non-cyclically-referenced object reaches zero, it will be garbage collected and its __del__ method will be called, as other answers have pointed out. From the same subsection as above:

    CPython implementation detail: CPython currently uses a reference-counting scheme with (optional) delayed detection of cyclically linked garbage, which collects most objects as soon as they become unreachable, but is not guaranteed to collect garbage containing circular references.

    But still, this is really precarious and something to not be really relied on, since as mentioned it is only guaranteed for objects that are not part of a cyclic reference graph. Also:

    Other implementations act differently and CPython may change. Do not depend on immediate finalization of objects when they become unreachable (so you should always close files explicitly).


    Bottom line

    From a purist point of view, the __del__ method is completely useless. From a slightly less purist point of view, it is still almost useless. From a practical point of view, it might be useful as a complementary—but never essential—feature of your code.