How do I get super () to work in this imperfect situation in python?

class ClsOne(object):
    def __init__(self):
        super(ClsOne, self).__init__()
        print "Here One"

class ClsTwo(ClsOne):
    def __init__(self):
        super(ClsTwo, self).__init__()
        print "Here Two"

class ClsThree(ClsTwo): # Refer to one blackbox object
    def __init__(self):
        # super(ClsThree, self).__init__()
        print "Here Three"

class ClsThreee(ClsTwo): # Refer to your custom object
    def __init__(self):
        super(ClsThreee, self).__init__()
        print "Here Threee"

class ClsFour(ClsThree, ClsThreee): # Multiple Inheritance
    def __init__(self):
        super(ClsFour, self).__init__()
        print "Here Four"

entity = ClsFour()

      

In this case, you are trying to combine ClsThree (which comes from the same compiled library and is very difficult to modify) and your own ClsThreee object. Since ClsThree forgets to call super () in its constructor, their kid cannot execute the ClsThreee constructor when it uses super ().

As a result, the result will be something like this:

Here Three
Here Four

      

Obviously I can manually call all the ClsFour bases and not use super (), but this is a little tricky when this problem is scattered throughout my codebase.

BTW this Blackbox stuff is PySide :)

ADDITION:

Thanks to @WillemVanOnsem and @RaymondHettinger, ClsFour's previous question has been resolved. But with some further research, I found that a similar issue in PySide does not have the same concept.

In the context of ClsFour, if you try to run:

print super(ClsFour, self).__init__

      

You'll get:

<bound method ClsFour.__init__ of <__main__.ClsFour object at 0x00000000031EC160>>

      

But in the following PySide context:

import sys
from PySide import QtGui

class MyObject(object):
    def __init__(self):
        super(MyObject, self).__init__()
        print "Here MyObject"

class MyWidget(QtGui.QWidget, MyObject):
    def __init__(self):
        super(MyWidget, self).__init__()

app = QtGui.QApplication(sys.argv)
widget = MyWidget()
print super(MyWidget, widget).__init__

      

Result:

<method-wrapper '__init__' of MyWidget object at 0x0000000005191D88>

      

It does not print "MyObject is here" and the init super () attribute is of a different type. I used to try to simplify this problem as ClsFour. But now I think it's not exactly the same.

I am guessing the problem is in the shiboken library, but I'm not sure.

TIPS:

The problem also occurs in the context of PyQt, but you can make MyObject inherit from QObject for a solution. This solution is useless in PySide.

+3


source to share


3 answers


super()

is a proxy object that uses Method Resolution Order (MRO) to determine which method to call when a call is made super()

.

If we check __mro__

of ClassFour

, we get:

>>> ClsFour.__mro__
(<class '__main__.ClsFour'>, <class '__main__.ClsThree'>, <class '__main__.ClsThreee'>, <class '__main__.ClsTwo'>, <class '__main__.ClsOne'>, <type 'object'>)

      

Or made it shorter (not Python output):

>>> ClsFour.__mro__
(ClsFour, ClsThree, ClsThreee, ClsTwo, ClsOne, object)

      

Now super(T,self)

is the proxy that uses the MRO from (but excluding) T

. So it means that there super(ClsFour,self)

is a proxy object that works with:

(ClsThree, ClsThreee, ClsTwo, ClsOne, object)  # super(ClsFour,self)

      

What happens if you ask for an attribute (method is also an attribute) of a class is that Python will go through the MRO and check if this element has such an attribute. So first check if the ClsThree

attribute has __init__

, if it won't keep looking for it in ClsThreee

and so on. From the moment it finds such an attribute, it will stop and return it.

So the methodsuper(ClsFour,self).__init__

will returnClsThree.__init__

. MRO is also used to find methods, attributes, etc. that are not defined at the class level. Therefore, if you use self.x

and is x

not an attribute of an object or object ClsFour

, it will again go through the MRO in the search x

.



If you want to call __init__

all direct parents ClassFour

, you can use:

class ClsFour(ClsThree, ClsThreee):
    def __init__(self):
        # call *all* *direct* parents __init__
        for par in ClsFour.__bases__:
            par.__init__(self)

      

This is probably the most elegant one as if the bases change it will still work. Please note that you must make sure that __init__

there is one for each parent. Since it is, however, defined at the level object

, we can safely assume this. However, for other attributes, we cannot make this assumption.

EDIT . Remember that as a result super()

need not necessary for parents, grandparents, and / or the ancestors of this class. But to the parent classes of the object.

super(ClsThree,self)

in the class ClsThree

, will be given as an object ClsFour

, works with the same mro (as it takes mro

from self

). So, super(ClsThree,self)

will check the following sequence of classes:

(ClsThreee, ClsTwo, ClsOne, object)

      

For example, if we write (outside the scope of any class) super(ClsTwo,entity).__init__()

, we get:

>>> super(ClsTwo,entity).__init__()
Here One
>>> super(ClsThree,entity).__init__()
Here Two
Here Threee
>>> super(ClsThreee,entity).__init__()
Here Two
>>> super(ClsFour,entity).__init__()
Here Three

      

+3


source


Summary

Since ClsThree forgets to call super () in its constructor, their kid cannot execute the ClsThreee constructor when it uses super ().

This is described in the "How to enable a non-commercial class" section in the Super Considered Super section .

The key is to create an adapter class that plays by the rules to act in concert by calling super (). Use this adapter for original grade packaging.

Processed code

The code below AdaptThree

is a new adapter class and ClsFour now inherits from AdaptThree instead of the original class that does not support Blackbox.



class ClsOne(object):
    def __init__(self):
        print "Here One"

class ClsTwo(ClsOne):
    def __init__(self):
        print "Here Two"

class ClsThree(ClsTwo): # Refer to one blackbox object
    def __init__(self):
        # super(ClsThree, self).__init__()
        print "Here Three"

class AdaptThree(object):
    def __init__(self):
        _three = ClsThree()
        super(AdaptThree, self).__init__()          

class ClsThreee(ClsTwo): # Refer to your custom object
    def __init__(self):
        super(ClsThreee, self).__init__()
        print "Here Threee"

class ClsFour(AdaptThree, ClsThreee): # Multiple Inheritance
    def __init__(self):
        super(ClsFour, self).__init__()
        print "Here Four"

entity = ClsFour()

      

Output:

Here Three
Here Two
Here Threee
Here Four

      

Other information

  • In OP's example, ClsTwo also looks inconsistent and should be wrapped as well.

  • Presumably these classes have methods other than initialization. The adapter classes will have to wrap and dispatch these calls as well.

  • Depending on the application, it may be easier to use composition rather than inheritance.

0


source


Regarding a problem specific to PySide / PyQt4, one solution is to ensure that any mixins always precede the Qt classes in the base class definition:

import sys
from PySide import QtGui

class MyObject(object):
    def __init__(self, parent=None, other=None):
        super(MyObject, self).__init__(parent)
        print "Here MyObject: %r" % other

class MyWidget(MyObject, QtGui.QWidget):
    def __init__(self, parent=None, other=None):
        super(MyWidget, self).__init__(parent, other)

app = QtGui.QApplication(sys.argv)
parent = QtGui.QWidget()
widget = MyWidget(parent, 'FOO')
print super(MyWidget, widget).__init__
# check that the QWidget.__init__ was called correctly
print 'widget.parent() is parent:', widget.parent() is parent

      

Output:

Here MyObject: 'FOO'
<bound method MyWidget.__init__ of <__main__.MyWidget object at 0x7f53b92cca28>>
widget.parent() is parent: True

      

(NB: PyQt5 has improved its Support for Collaborative Multiple Inheritance , so this question doesn't come up there).

0


source







All Articles