21

From the python docs:

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

Why not? What problems would occur if this guarantee were made?

Shea Levy
  • 5,237
  • 3
  • 31
  • 42
  • In which order would you call the `__del__()` methods for objects in the same scope? – Frédéric Hamidi Jan 31 '13 at 14:51
  • By the order in which those objects reference each other, or if there are cycles then you already have the "no `__del__()` in cycles" rule. – Shea Levy Jan 31 '13 at 15:04
  • For Python-the-language, this is not guaranteed because in some implementations it is not possible to implement. E.g. the Java Python port is dependent on what the JVM garbage collector does and cannot just change that. But I am not sure if that documentation is supposed to describe the language or the CPython implementation. As strubbly answered, it seems to be guaranteed in the newer CPython versions, but wasn't in older ones, most likely due to the difficulty of implementing it correctly and predictably. – JanKanis Dec 04 '17 at 16:09

5 Answers5

8

I'm not convinced by the previous answers here.

Firstly note that the example given does not prevent __del__ methods being called during exit. In fact, the current CPythons will call the __del__ method given, twice in the case of Python 2.7 and once in the case of Python 3.4. So this can't be the "killer example" which shows why the guarantee is not made.

I think the statement in the docs is not motivated by a design principle that calling the destructors would be bad. Not least because it seems that in CPython 3.4 and up they are always called as you would expect and this caveat seems to be moot.

Instead I think the statement simply reflects the fact that the CPython implementation has sometimes not called all destructors on exit (presumably for ease of implementation reasons).

The situation seems to be that CPython 3.4 and 3.5 do always call all destructors on interpreter exit.

CPython 2.7 by contrast does not always do this. Certainly __del__ methods are usually not called on objects which have cyclic references, because those objects cannot be deleted if they have a __del__ method. The garbage collector won't collect them. While the objects do disappear when the interpreter exits (of course) they are not finalized and so their __del__ methods are never called. This is no longer true in Python 3.4 after the implementation of PEP 442.

However, it seems that Python 2.7 also does not finalize objects that have cyclic references, even if they have no destructors, if they only become unreachable during the interpreter exit.

Presumably this behaviour is sufficiently particular and difficult to explain that it is best expressed simply by a generic disclaimer - as the docs do.

Here's an example:

class Foo(object):
    def __init__(self):
        print("Foo init running")

    def __del__(self):
        print("Destructor Foo")

class Bar(object):
    def __init__(self):
        print("Bar1 init running")
        self.bar = self
        self.foo = Foo()

b = Bar()

# del b

With the del b commented out, the destructor in Foo is not called in Python 2.7 though it is in Python 3.4.

With the del b added, then the destructor is called (at interpreter exit) in both cases.

strubbly
  • 3,347
  • 3
  • 24
  • 36
  • 4
    This answer is wrong. Python 3.4+ still makes no guarantee that destructors will be called. [Here's](https://ideone.com/2UvmrW) a simple example based on sebix's answer down the page. Python 3.4+ tries a bit harder than earlier versions did, but it's still pretty much just best-effort basis. – user2357112 Jul 24 '18 at 16:39
  • @user2357112 your example runs `__del__` in python 3.7 (not sure if it's actually guaranteed though). – patmanpato Mar 28 '19 at 23:34
5

If you did some nasty things, you could find yourself with an undeletable object which python would try to delete forever:

class Phoenix(object):
    def __del__(self):
        print "Deleting an Oops"
        global a
        a = self

a = Phoenix()

Relying on __del__ isn't great in any event as python doesn't guarantee when an object will be deleted (especially objects with cyclic references). That said, perhaps turning your class into a context manager is a better solution ... Then you can guarantee that cleanup code is called even in the case of an exception, etc...

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • Hm... I agree with your second example, but your first example can be avoided by seeing that `a` holds a reference to `lst` so a should be deleted first, and the `RuntimeError` issue doesn't occur because the docs explicitly say exceptions during destructors behave differently. But the second example is ridiculous, why limit things for that stupid use case? – Shea Levy Jan 31 '13 at 15:01
  • Also, add a `del a` after your second case and you'll see the destructor called (three times on my machine) – Shea Levy Jan 31 '13 at 15:03
  • 2
    @SheaLevy -- The point is that python provides an excellent facility for automatically handling cleanup (look into context managers). It's just a safer way of handling things. – mgilson Jan 31 '13 at 15:09
  • 1
    But context managers don't cover all use cases. Sometimes you need a resource open across multiple threads and only want to cleanup after all of the threads are done, or sometimes you have a long-lived resource that's needed in multiple places that can't all be covered in a single scope. – Shea Levy Jan 31 '13 at 15:12
  • 1
    Anyway, this seems the right answer from the perspective of "why it is this way", even if I'm not convinced it needs to be. – Shea Levy Jan 31 '13 at 15:14
  • 1
    @SheaLevy I don't agree that this is right - and I've added my own answer. For a start, destructors *are* called on exit almost always, so it can't be fundamentally difficult - and the given example works fine. And Python 3 always works. It's just that Python 2 doesn't call the garbage collector again *after* deleting all the globally scoped names. And rather than explain that in detail the documenters added a generic exclusion. – strubbly Mar 06 '16 at 08:41
  • @SheaLevy I think context managers can be made to cover all cases without too much problem. Your first example, of cleanup after all threads are done, sounds quite straightforward to implement with a context manager. Your second also doesn't make sense as an objection: Can't all be covered by a single scope? Not even the scope of 'main()' ? – Jonathan Hartley Aug 12 '16 at 17:15
1

I don't think this is because doing the deletions would cause problems. It's more that the Python philosophy is not to encourage developers to rely on the use of object deletion, because the timing of these deletions cannot be predicted - it is up to the garbage collector when it occurs.

If the garbage collector may defer deleting unused objects for an unknown amount of time after they go out of scope, then relying on side effects that happen during the object deletion is not a very robust or deterministic strategy. RAII is not the Python way. Instead Python code handles cleanup using context managers, decorators, and the like.

Worse, in complicated situations, such as with object cycles, the garbage collector might not ever detect that objects can be deleted. This situation has improved as Python has matured. But because of exceptions to the expected GC behaviour like this, it is unwise for Python developers to rely on object deletion.

I speculate that interpreter exit is another complicated situation where the Python devs, especially for older versions of Python, were not completely strict about making sure the GC delete ran on all objects.

Jonathan Hartley
  • 15,462
  • 9
  • 79
  • 80
1

One example where the destructor is not called is, if you exit inside a method. Have a look at this example:

class Foo(object):
    def __init__(self):
        print("Foo init running")

    def __del__(self):
        print("Destructor Foo")


class Bar(object):
    def __init__(self):
        print("Bar1 init running")
        self.bar = self
        self.foo = Foo()

    def __del__(self):
        print("Destructor Bar")

    def stop(self):
        del self.foo
        del self
        sys.exit(1)

b = Bar()
b.stop()

The output is:

Bar1 init running
Foo init running
Destructor Foo

As we destruct foo explicitly, the destructor is called, but not the destructor of bar!

And, if we do not delete foo explicitly, it is also not destructed properly:

class Foo(object):
    def __init__(self):
        print("Foo init running")

    def __del__(self):
        print("Destructor Foo")


class Bar(object):
    def __init__(self):
        print("Bar1 init running")
        self.bar = self
        self.foo = Foo()

    def __del__(self):
        print("Destructor Bar")

    def stop(self):
        sys.exit(1)

b = Bar()
b.stop()

Output:

Bar1 init running
Foo init running
sebix
  • 2,943
  • 2
  • 28
  • 43
  • 1
    You don't even have to exit within a method. [Calling `exit` at module level](https://ideone.com/2UvmrW) prevents the `__del__` call too. – user2357112 Jul 24 '18 at 16:27
0

Likely because most of programmers would assume that destructors should only be called on dead (already unreachable) objects, and here on exit we would invoke them on live objects.

If it the developer has not been expecting a destructor call on the live object, some nasty UB may result. At least, something must be done to force-close the application after time out if it hangs. But then some destructors may not be called.

Java Runtime.runFinalizersOnExit has been deprecated because of the same reason.

Audrius Meškauskas
  • 20,936
  • 12
  • 75
  • 93