7

There is a simple program in python3:

from PyQt4 import QtCore
import PyQt4

class Bar(object):
    def __init__(self):
        print("Bar start")
        super(Bar, self).__init__()
        print("Bar end")

class FakeQObject(object):
    def __init__(self):
        print("FakeQObject start")
        super(FakeQObject, self).__init__()
        print("FakeQObject end")

class Foo(QtCore.QObject, Bar):
#class Foo(FakeQObject, Bar):
    def __init__(self):
        print("Foo start")
        super(Foo, self).__init__()
        print("Foo end")


print(Foo.__mro__)
print(PyQt4.QtCore.PYQT_VERSION_STR)
f = Foo()

a) When class Foo inherits from QtCore.QObject and Bar we get:

(<class '__main__.Foo'>, <class 'PyQt4.QtCore.QObject'>, <class 'sip.wrapper'>, <class 'sip.simplewrapper'>, <class '__main__.Bar'>, <class 'object'>)
4.9.4
Foo start
Foo end

b) When class Foo inherits from FakeQObject and Bar we get:

(<class '__main__.Foo'>, <class '__main__.FakeQObject'>, <class '__main__.Bar'>, <class 'object'>)
4.9.4
Foo start
FakeQObject start
Bar start
Bar end
FakeQObject end
Foo end

The question is: why in the a) case, Bar init is not called?

I found similar question here pyQt4 and inheritance but there are no good answers.

Thanks in advance!

Community
  • 1
  • 1
vitvlkv
  • 782
  • 7
  • 13
  • 4
    I suspect it is because `QtCore.QObject` doesn't use the cooperative super.`__init__`. BTW, in Python 3, you don't need `super(Foo, self)`; `super()` should be sufficient. – nneonneo Sep 10 '12 at 12:49
  • Yes, but PyQt site http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/gotchas.html says: "In versions of PyQt earlier than v4.5 there were restrictions on the use of super with PyQt classes. These restrictions no longer apply with v4.5 and later." So, this is a bug of PyQt, isn't it? – vitvlkv Sep 10 '12 at 14:06
  • Thanks for super() without params, didn't know about it.. – vitvlkv Sep 10 '12 at 14:07
  • Is there a reason you can't move `Bar` before `QObject` in the inheritance order? – ecatmur Sep 10 '12 at 14:58
  • In my project I already did so.. But It's unnatural and sad, that limitations of language/library/whatever dictate application design. – vitvlkv Sep 10 '12 at 16:03
  • Note that this is not a limitation of PyQt4 and/or Python3: the same behaviour will occur with PySide, and with Python2. – ekhumoro Sep 10 '12 at 16:44
  • Unrelated to the problem: In Py3k no need to explicitly derive from `object`. You can omit `object` as it is the default. Your print of `__mro__` won't change. Saves typing and confusion. Keep `(object)` only for scripts that need to run in both Py2 and Py3. Doesn't address your question, though :-/ – cfi Sep 11 '12 at 07:11

1 Answers1

3

Along with @nneonneo I also suspect that QtCore.QObject doesn't use the cooperative super.__init__. If it did, you wouldn't have this problem.

However, you should be aware that at some point one of the base classes cannot use cooperative super because object won't have the method. Consider:

class Base():
    def __init__(self):
        print("initializing Base")
        super().__init__()
    def helper(self, text):
        print("Base helper")
        text = super().helper(text)
        text = text.title()
        print(text)

class EndOfTheLine():
    def __init__(self):
        print("initializing EOTL")
        super().__init__()
    def helper(self, text):
        print("EOTL helper")
        text = super().helper(text)
        return reversed(text)

class FurtherDown(Base, EndOfTheLine):
    def __init__(self):
        print("initializing FD")
        super().__init__()
    def helper(self, text):
        print(super().helper(text))

test = FurtherDown()
print(test.helper('test 1 2 3... test 1 2 3'))

and the output:

initializing FD
initializing Base
initializing EOTL
Base helper
EOTL helper
Traceback (most recent call last):
  File "test.py", line 28, in <module>
    print(test.helper('test 1 2 3... test 1 2 3'))
  File "test.py", line 25, in helper
    print(super().helper(text))
  File "test.py", line 7, in helper
    text = super().helper(text)
  File "test.py", line 17, in helper
    text = super().helper(text)
AttributeError: 'super' object has no attribute 'helper'

So, whichever class is going to be the End of the Line needs to not call super. Because there are other Qt methods you might want to override, that dictates that the Qt class must be the last one in the class header. By not having __init__ use cooperative super, even though it could, Qt is avoiding bugs further down when some other method is overridden.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237