Cooperative multi-inheritance works with __init__ but not with __new__

Russell Warren russ at perspexis.com
Sat Jan 30 19:10:14 GMT 2021


>
> I'm not sure how you expect this to work.


I just expect it to work the same way that __new__ work always works, which
typically uses super() to invoke __new__ on the parent/next class. This
requires super() cooperation through the MRO.

Copying directly from the python documentation for __new__
<https://docs.python.org/3/reference/datamodel.html#object.__new__> [1]:

*Typical implementations create a new instance of the class by invoking the
> superclass’s __new__() method using super().__new__(cls[, ...]) with
> appropriate arguments and then modifying the newly-created instance as
> necessary before returning it.*


... but if base classes involved haven't been written to play nicely with
super(), this obviously breaks as soon as an MRO traversal hits a
non-compliant class.  At least one of `PyQt5.QtCore.QObject`,
`sip.wrapper`, or `sip.simplewrapper` is such a non-compliant super()
obstacle.

Cooperative multi-inheritance is only really useful for methods that don't
> return a result.


Not true.  __new__ is a perfect example of this.


> You can't assume that the last type in the MRO is the one that actually
> creates the object


Of course you can't. There is no need to make any such assumption on MRO,
since any code that tries to do this is destined to fail at some point.
This applies to any super() usage, not just object creation with __new__.

However, one relevant side note here is that you *can* obviously assume
that a metaclass has been involved in creating the object's class, no
matter what the resulting MRO.  This is relevant for my specific case.  I
have a wrappertype-derived metaclass that constructs my classes and (among
other things) adds some class-level attrs.  Then the instance-level __new__
expects those class attrs to be there when it runs.  This is obviously 100%
reliable, no matter the MRO.  The class attrs will definitely be there when
instance.__new__ is invoked. Unfortunately, since `sip.wrappertype` (and/or
the other two PyQt5 classes) aren't written to comply with super()
properly, what is not reliable is whether all instance.__new__
implementations in the MRO are invoked.

As with other cases where classes are unfortunately not super()-compliant,
you can hack around the problem through careful MRO manipulation (base
class ordering) or by wrapping non-compliant classes with adapters.
However, it would be much cleaner/nicer if PyQt5 worked with super() and
such hacks were not required.

(and therefore all preceding types can simply return the value returned by
> their call to super()).


"preceding" depends entirely on MRO order, which we agree can't be
assumed!  super() compliance makes this a non-issue. super() non-compliance
breaks things.

simplewrapper.__new__() has created the object so why would it call a
> super-class implementation?


To comply with super()! To support cooperative multiclassing! :)  You
wouldn't argue the same for __init__, so why do so for __new__?

Russ

[1] https://docs.python.org/3/reference/datamodel.html#object.__new__
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20210130/4145f934/attachment.htm>


More information about the PyQt mailing list