[PyQt] Fwd: Mixin classes and PyQt4

Phil Thompson phil at riverbankcomputing.com
Fri Feb 21 15:57:44 GMT 2014


On 21-02-2014 9:55 am, Martin Teichmann wrote:
> Hi Phil, Hi Baz, Hi List,
>
> sorry, I didnt 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, its just that I was to lazy to install
> Qt5 on my machine...
>
>> At the moment (having just started to think about it again) Im
> 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 dont 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, its 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 thats
> 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 its only fair to point out that this will
> probably 
>> break code for at least some people, if this blog post is anything
> to go by:
>>
>>  
>  http://trevorius.com/scrapbook/python/pyqt-multiple-inheritance/ [1]
>
>>
>> Of course, you could say that they got what they deserved for
> trying 
>> to do something in PyQt that just wouldnt 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 lets 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 pythons 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 -
> thats
> 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 dont
> 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 lets talk about my second patch. It enforces PyQt classes
> to go last, and not inherit from incompatible classes. Thats 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.

I agree that your first patch will work. My objection is simply that 
the behaviour it introduces is too different to that of C++/Qt. I admit 
that it is a subjective opinion, after all Python is not C++ - but Qt is 
Qt.

Given my objection, PyQt should detect when the programmer tries to 
inherit from more than one Qt class, but it doesn't and introducing such 
a check now will break existing code. At this stage in their respective 
lives that is acceptable for PyQt5 but not for PyQt4.

I don't agree that specifying the Qt class as the last base class 
should be enforced - although I accept that it might be strongly 
recommended.

In summary, I won't make any changes that affect the behaviour of 
PyQt4. I may add a test to PyQt5 that checks for inheriting from 
multiple Qt classes (unless all but one class are Qt interface classes). 
If so I will also add the ability to define signals and properties in 
non-Qt classes to keep Baz happy.

Phil


More information about the PyQt mailing list