[PyQt] Fwd: Mixin classes and PyQt4

Martin Teichmann martin.teichmann at gmail.com
Fri Feb 21 09:55:36 GMT 2014

Hi Phil, Hi Baz, Hi List,

sorry, I didn't have time to respond earlier.

The changes I did are to SIP, and I guess, Phil, you do
intend to have more releases of SIP? So, all I write
applies to PyQt5, too, it's just that I was to lazy to install
Qt5 on my machine...

> At the moment (having just started to think about it again) I'm against
> your original patch as it is too different to the C++ behaviour.

well, thats the difference between Python and C++. In C++, the
order of base classes defines the memory layout of the combined
class, the members of the base classes are simply put one after
the other in memory. Qt needs its classes to go first, probably they
cannot deal with the situation where their data members end up
somewhere they don't expect. In Python, thats all different. The
memory layout is defined by the biggest base class, no matter where
it appears in the list of bases. The inheritance from two classes
with incompatible memory layout is prohibited, and Python will raise
an error (yes, C++ is more flexible here). This should have happened
with PyQt classes, it's just that PyQt uses pointers to the C++ objects,
and so the size of the Python object does not change, so Python
incorrectly does not detect the fact that the memory layout for two
different Qt base classes is, in fact, incompatible. Detecting this is
the goal of my second patch.

The order of base classes in Python is, on the other hand, by no
means irrelevant: it defines the order in which methods are called.
So the first base class will overwrite methods of the second, etc.
This is why mixin classes have to go before the classes they
are supposed to mix in: they are supposed to overwrite their
methods, and not the other way around. Now, often an overwriting
method intends to call the overwritten method, in Python that's
nowadays often done by calling super(). Unfortunately, PyQt does
not do that (except, sometimes, for __init__). This is why PyQt
classes naturally go last in the list of base classes.

> On the face of it, this seems like an entirely good thing to do.
> However, I suppose it's only fair to point out that this will probably
> break code for at least some people, if this blog post is anything to go
>    http://trevorius.com/scrapbook/python/pyqt-multiple-inheritance/
> Of course, you could say that they got what they deserved for trying
> to do something in PyQt that just wouldn't be allowed in Qt itself. But
> whatever the rights and wrongs, there are going to be a few people
> who might hope that this patch is only applied if the issue with custom
> signals can be resolved first.

Thanks for pointing me to this website. Well, those are exactly the
usecases I intended to solve. So, my patch will keep all the code
presented on that website working, while resolving all but one of
the issues mentioned there. This website is a very good analysis
of the problem, so let's go through it:

1. The first inherited widget must have the deepest base-class.

This is the issue I solve with my first patch. Instead of asking the
programmer to put the deepest base-class first, I am looking in the
bases list for the deepest base class. So as long as the programmer
has followed the advice, nothing will change. If not, the code would
not have worked before and now will work properly.

1a. You cannot use super(). (mentioned further down on the website)

This is a non astonishing corollary of No 1. The rule to put the deepest
base class first breaks python's method resolution order, because
it tells python that the deepest base-classes methods should be called
first - a very strange concept of a base class. This is why I am
lifting that rule for enabling putting the deepest base class last - that's
where it belongs. So, with my patch super() works as intended.

2. Only additional signals defined in the first inherited widget are used.

This is slightly modified with my patch: only additional signals in the
first base class and the deepest base class are used. I actually don't
know how that happened. Only important thing: old code will still work.

3. Name clashes are resolved by calling the first class’s methods

This is the same problem as No 1a.

Now let's talk about my second patch. It enforces PyQt classes
to go last, and not inherit from incompatible classes. That's the
right thing to do according to all I wrote above. Unfortunately it
will break code from the mentioned website. So for backwards
compatiblity, it should only give a warning. It also breaks the
new __init__-calls-super() from PyQt5. But as I wrote above,
this is also weird, as it expects mixin classes to go behind
PyQt classes, which is just the wrong way around. So it
should be deprecated and removed from the documentation
before too many people start using this feature. For those who
did: just inverse the order of base classes, and all will work
as expected, with both the current version of PyQt and my
modified version.

So, to conclude: my patches are still relevant as they applies to SIP,
the first one does not break old code but will lift unecessary limitations,
while the second one breaks flawed old code and thus should only
raise a warning.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20140221/6489fbec/attachment.html>

More information about the PyQt mailing list