6

When is the below python static classs variable garbage collected? I was expecting to see the message from the static variable foo destructor.

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

    def __del__(self):
        print "Destructor Foo"

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

    def __del__(self):
        print "Destructor Bar"

bar_obj = Bar()

The output is (Python 2.7):

Foo init running
Bar init running
Destructor Bar

I was expecting:

Foo init running
Bar init running
Destructor Foo
Destructor Bar
A.P.
  • 91
  • 1
  • 6

1 Answers1

3

So we would expect the reference to the foo object to be deleted when the Bar class is deleted. And that is generally what happens. If you try

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

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

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

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


def f():
    bar_obj = Bar()

f()
del Bar

I get

Foo init running
Bar init running
Destructor Bar
Destructor Foo

and you can see both destructors called in both Python 2.7 and Python 3.4. However, in Python 2.7, Bar is not being properly destructed during program close down. As the docs say:

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

Why is Bar not destructed during interpreter exit?

It seems likely that the class in Python 2.7 is not destructed because of circular references (see below). In Python 3.4 (after PEP 442) objects with circular references are reliably destructed (even if they have __del__ methods) and this may explain the change.

However, this does not completely explain the difference because although the class is in a reference cycle, the class itself does not have a destructor.

It seems that in Python 2 objects with circular references are not reliably destructed during interpreter exit, whereas they are in Python 3.4. I give some more information here.

Edit (more detail about circular references):

Classes contain cyclic references back to themselves, firstly via their dictionary:

MyClass.__dict__['__dict__'].__objclass__ == MyClass

and secondly via their MRO details:

MyClass in MyClass.__mro__
Community
  • 1
  • 1
strubbly
  • 3,347
  • 3
  • 24
  • 36
  • This does not explain why it's not deleted. – luoluo Oct 12 '15 at 07:55
  • It is a feature of Python 2.7 but I don't fully understand the design rationale. – strubbly Oct 12 '15 at 08:05
  • Hmm - more thought and I have come up with a plausible explanation – strubbly Oct 12 '15 at 08:18
  • Can you explain where is the circular reference? Please elaborate a bit on your last paragraph. I don't fully understand it. – A.P. Oct 14 '15 at 07:14
  • @A.P. Well I'm not sure - I got it from [here](http://stackoverflow.com/a/5609683/1295678). But I'm looking into it. Certainly all classes have a link to their Metaclass and some Metaclasses have links to classes but not sure if all do? – strubbly Oct 14 '15 at 07:46
  • Bar instance (bar_obj) is destructed (I see the __del__ execution). The Foo instance (foo) which is part of the Bar class as a static variable is not destructed (i don't see the __del__). – A.P. Oct 21 '15 at 09:15
  • @A.P. Works for me on 2.7.10. Are you deleting `bar_obj`? In my code it is deleted when the execution of `f` ends. I'll paste the exact code into my answer and you can test... – strubbly Oct 21 '15 at 11:28