I found something interesting, here is a snippet of code:
class A(object):
def __init__(self):
print \"A init\"
def __del__(self):
p
TL;DR: this is an old issue in CPython, that was finally fixed in CPython 3.4. Objects kept live by reference cycles that are referred to by module globals are not properly finalized on interpreter exit in CPython versions prior to 3.4. New-style classes have implicit cycles in their type
instances; old-style classes (of type classobj
) do not have implicit reference cycles.
Even though fixed in this case, the CPython 3.4 documentation still recommends to not depend on __del__ being called on interpreter exit - consider yourself warned.
New style classes have reference cycles in themselves: most notably
>>> class A(object):
... pass
>>> A.__mro__[0] is A
True
This means that they cannot be deleted instantly*, but only when the garbage collector is run. Since a reference to them is being held by the main module, they will stay in memory until the interpreter shutdown. At the end, during the module clean-up, all the module global names in the main are set to point to None
, and whichever objects had their reference counts decreased to zero (your old-style class for example) were also deleted. However, the new-style classes, having reference cycles, would not be released/finalized by this.
The cyclic garbage collector would not be run at the interpreter exit (which is allowed by the CPython documentation:
It is not guaranteed that
__del__()
methods are called for objects that still exist when the interpreter exits.
Now, old-style classes in Python 2 do not have implicit cycles. When the CPython module cleanup/shutdown code sets the global variables to None
, the only remaining reference to class B
is dropped; then B
is deleted, and the last reference to a
is dropped, and a
too is finalized.
To demonstrate the fact that the new-style classes have cycles and require a GC sweep, whereas the old-style classes do not, you can try the following program in CPython 2 (CPython 3 does not have old-style classes any more):
import gc
class A(object):
def __init__(self):
print("A init")
def __del__(self):
print("A del")
class B(object):
a = A()
del B
print("About to execute gc.collect()")
gc.collect()
With B
as new-style class as above, the output is
A init
About to execute gc.collect()
A del
With B
as old-style class (class B:
), the output is
A init
A del
About to execute gc.collect()
That is, the new-style class was deleted only after gc.collect()
even though the last outside reference to it was dropped already; but the old-style class was deleted instantly.
Much of this is already fixed in Python 3.4: thanks to PEP 442, which included the module shutdown procedure based on GC code. Now even on interpreter exit the module globals are finalized using the ordinary garbage collection. If you run your program under Python 3.4, the program will print
A init
A del
Whereas with Python <=3.3 it will print
A init
(Do note that other implementations still might or might not execute __del__
at this moment, regardless of the version of them being above, at, or below, 3.4)