[PySide] Destructors are not called

1,058 views
Skip to first unread message

Thomas Sturm

unread,
Oct 20, 2010, 8:00:20 AM10/20/10
to PyS...@lists.openbossa.org
Dear list,

I am quite puzzled about my piece of code below (and attached), where none of the two destructors is called when terminating the program via closeEvent(). The problem disappears when I remove the assignment "self.main = parent" from MyMenuBar.__init__(). I have checked this on Mac OSX and Ubuntu.

I would actually expect the following to happen with or without the questionable assignment:

1. The MyMenuBar instance is known as a child of the MyMainWindow instance in the Qt widget hierarchy. It is thus deleted by Qt on the closeEvent().

2. On deleting that instance its reference self.main disappears, and it cannot play a role for what happens subsequently.

3. Since the instance of MyWorker lives outside the Qt widget hierarchy, I would expect its destructor to be called by Python on termination of the program.

Any clarification is highly appreciated! I am glad to have reduce my way more complicated situation to that small example, but now I am clueless.

NB: I know that there is a parent() method, and that in that situation the assignment is pointless etc.

Thomas

#!/usr/bin/env python

import sys

from PySide.QtGui import QApplication
from PySide.QtGui import QMainWindow
from PySide.QtGui import QMenuBar

class MyMainWindow(QMainWindow):
def __init__(self,parent=None):
super(MyMainWindow,self).__init__(parent)
self.worker = MyWorker()
self.setMenuBar(MyMenuBar(self))
self.show()
self.raise_()

class MyMenuBar(QMenuBar):
def __init__(self,parent=None):
super(MyMenuBar,self).__init__(parent)
self.main = parent # This line causes the problem

def __del__(self):
print "in MyMenuBar.__del__()"

class MyWorker(object):
def __del__(self):
print "in MyWorker.__del__()"

app = QApplication(sys.argv)
mainwindow = MyMainWindow()
sys.exit(app.exec_())
--
Dr. habil. Thomas Sturm
Departamento de Matematicas, Estadistica y Computacion
Universidad de Cantabria, Santander, Spain
Avda. Los Castros s/n, Room 1072, +34 693 251058
http://personales.unican.es/sturmt/

desctructors.py

Renato Araujo Oliveira Filho

unread,
Oct 20, 2010, 10:09:39 AM10/20/10
to Thomas Sturm, PyS...@lists.openbossa.org
Hi Thomas,

I have already found this problem in the past, this is not a PySide
problem this is a problem on python GC its not work well with cyclic
dependency and object which implement __del__ function, I do not know
the exactly problem on GC but I can reproduce your problem with this
simple code.


You can have more info about python GC on:
http://docs.python.org/library/gc.html

class MyObject(object):
def __init__(self, p):
self._parent = p
print "created", self

def __del__(self):
print "del", self


class MyObject2(object):
def __init__(self):
self._child = MyObject(self)
print "created", self

def __del__(self):
print "del", self


o = MyObject2()
del o

Then I recommend to you use a weak ref when you will store the parent
object, this will avoid the cyclic dependency. Or someone who now more
python GC can tell you how to solve this in other way, but I normally
use a proxy object.


You can get more info about weak ref from here:
http://docs.python.org/library/weakref.html


BR

> _______________________________________________
> PySide mailing list
> PyS...@lists.openbossa.org
> http://lists.openbossa.org/listinfo/pyside
>
>

--
Renato Araujo Oliveira Filho
Instituto Nokia de Tecnologia - INdT
Mobile: +55 (81) 8704-2144
_______________________________________________
PySide mailing list
PyS...@lists.openbossa.org
http://lists.openbossa.org/listinfo/pyside

Sebastian Wiesner

unread,
Oct 21, 2010, 3:48:02 AM10/21/10
to Thomas Sturm, PyS...@lists.openbossa.org
2010/10/20 Thomas Sturm <st...@redlog.eu>:

> 2. On deleting that instance its reference self.main disappears, and it cannot play a role for what happens subsequently.
It doesn't, references do not magically disappear. On the contrary,
the key point of references is, that the object referred to remains
alive as long as the reference is valid.

It may happen, that Qt deletes the underlying C++ object, but Python
doesn't know this, and still keeps the wrapper object alive, because
there are still valid references to it. As long as there is any
reference, this object is still alive, and its __del__ method will
consequently not be invoked.

Pythons memory management should be considered non-deterministic, just
like with other managed platforms. __del__ is therefore not the
Python equivalent to a C++ destructor, and it should not be used this
way. I do know your indent in using __del__ in this specific
situation, but avoid it, if at all possible. Move cleanup work to
other methods (e.g. a event callback or a slot connected to the
"deleted" signal), which are guaranteed to be invoked in a certain
situation.

If you can't, then at least use a weak reference to refer to objects,
whose lifetime should not be controlled by Python.

> 3. Since the instance of MyWorker lives outside the Qt widget hierarchy, I would expect its destructor to be called by Python on termination of the program.

As stated in the documentation [1], the Python interpreter does not
guarantee invocation of "__del__()" for any object still alive at
interpreter termination.

Sebastian Wiesner

[1] http://docs.python.org/reference/datamodel.html#object.__del__

Thomas Sturm

unread,
Oct 28, 2010, 6:33:47 PM10/28/10
to Sebastian Wiesner
Sorry, I am returning to this with some delay.

> It may happen, that Qt deletes the underlying C++ object, but Python
> doesn't know this, and still keeps the wrapper object alive, because
> there are still valid references to it. As long as there is any
> reference, this object is still alive, and its __del__ method will
> consequently not be invoked.

OK. This was exactly my mistake: I believed that the wrapper and Qt itself were so closely coupled that Qt's deletions would affect the corresponding Python objects.

In fact, when things did not work I intuitively put an explicit del into the close event of the main window. This resolved the problem but I never understood why. Now I do - thanks!

>
> Pythons memory management should be considered non-deterministic, just
> like with other managed platforms. __del__ is therefore not the
> Python equivalent to a C++ destructor, and it should not be used this
> way. I do know your indent in using __del__ in this specific
> situation, but avoid it, if at all possible. Move cleanup work to
> other methods (e.g. a event callback or a slot connected to the
> "deleted" signal), which are guaranteed to be invoked in a certain
> situation.

The class in question is Python-wrapped C-Code, which spawns a process. That process is a Lisp System, which occupies a considerable amount of memory. I would thus be very happy about any advice how to arrange my code in such a way that the destructor - or more generally the corresponding code for the deletion of the questionable process - is called on termination of the Python program under any circumstances - including abnormal termination due to bugs, kill -9 from outside, or whatever. Is this possible?

Thomas

Sebastian Wiesner

unread,
Oct 29, 2010, 10:56:05 AM10/29/10
to Thomas Sturm, pys...@lists.openbossa.org
2010/10/29 Thomas Sturm <st...@redlog.eu>:

> The class in question is Python-wrapped C-Code, which spawns a process. That process is a Lisp System, which occupies a considerable amount of memory. I would thus be very happy about any advice how to arrange my code in such a way that the destructor - or more generally the corresponding code for the deletion of the questionable process - is called on termination of the Python program under any circumstances - including abnormal termination due to bugs, kill -9 from outside, or whatever. Is this possible?

In case of "kill -9" it is impossible, this signal cannot be caught by
a running process. Other cases (e.g. unexpected exceptions or the
like) can be handled, but do *not* use __del__ for this purpose!
Implement a deterministic resource management, e.g. using a context
manager, or by providing an explicit ".close()" or ".cleanup()" (or
whatever you want to call it) method, and call this method at the
appropriate places.

Sebastian Wiesner

Reply all
Reply to author
Forward
0 new messages