8

I understand that Python doesn't guarantee the order of destruction of objects at the end of the program, or even that it will happen.

So I understand that a class's destructor can't rely on global variables, including other modules.

But I would have thought that objects of the class would have to be destroyed before the class is destroyed. Apparently not:

class A(object):
    count = 0
    def __init__(self):
        A.count += 1
        print 'Creating object, there are now', A.count
    def __del__(self):
        A.count -= 1
        print 'Destroying object, there are now', A.count

a1 = A()
a2 = A()

On Windows 7 x64 Python v2.7.3 I get:

Creating object, there are now 1
Creating object, there are now 2
Exception AttributeError: "'NoneType' object has no attribute 'count'" in <bound
 method A.__del__ of <__main__.A object at 0x0234B8B0>> ignored
Exception AttributeError: "'NoneType' object has no attribute 'count'" in <bound
 method A.__del__ of <__main__.A object at 0x0234BE90>> ignored

I understand the implications of How do I correctly clean up a Python object?. But this case is for class (shared or 'static' for my C++ friends) variables. Surely the instance has a reference to the class variable, which should not be destroyed first as we still have a reference to it?

Is it a problem or a mistake to have the class destroyed before objects of that class? Maybe my question is 'Where are class-shared variables actually stored?"

(Yes, I understand that this can be rectified using a context manager, or even simply:

del a1
del a2

to destroy the object before the class is destroyed.)

Community
  • 1
  • 1
Peter
  • 401
  • 5
  • 7

1 Answers1

6

Maybe my question is 'Where are class-shared variables actually stored?"

If that is your question, the answer is "on the class".

To address your larger question: As you noted, __del__ can't rely on global variables — but A is a global variable, so you can't rely on it. Interestingly, I can't find an official statement of this in the docs, but there are various references on the internet to this fact that __del__ can't use global variables. Here is one:

When a Python program terminates the global variables in each module are set to None. The order in which this happens it undefined

It will work if you access the count via self.__class__.count instead of A.count. (This is also why it works when you use self.count.)

BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • 1
    what about `.__class__` on the instance? – FatalError Mar 14 '13 at 04:22
  • 1
    @FatalError: You're right, the reference issue is a red herring. The issue is not that the class itself has been deleted, but that the global variable referencing it has been set to None. I edited my answer. – BrenBarn Mar 14 '13 at 04:40
  • 2
    Curiously, I get the same behavior when I try it with `.__class__`. It appears as if it's also set to `None` before calling `__del__` in this case, which is kind of surprising. – FatalError Mar 14 '13 at 04:46
  • 1
    @FatalError: Did you change both the `-=` and the `print` to use `self.__class__`? I thought I saw what you were seeing too, but I was still doing `print A.count`. – BrenBarn Mar 14 '13 at 05:07
  • Whoops, you nailed it, that's exactly what I was doing ;). The world feels more sane again. – FatalError Mar 14 '13 at 05:16
  • 2
    With the environment setting `PYTHONVERBOSE=2` you can see the order of module cleanup and attribute clearing. – Eryk Sun Mar 14 '13 at 05:50
  • But note that self.__class__.count and self.count are not the same thing. I've edited my post to make it clear. – Peter Mar 14 '13 at 05:55
  • @Peter: That is true, but that is tangential to the issue. If you change everything to use `self.count`, then you are changing the nature of the code so the class-attribute issue is no longer an issue. If you only *read* the value of `self.count` (in the final print) then it is in fact the same as `self.__class__.count` in this case. Also, don't edit someone else's answer to clarify your own points. – BrenBarn Mar 14 '13 at 06:06