Search code examples
python-3.xmultiple-inheritancesuper

What does the super() point to on earth?


In the post ,super() was described as: describe super()

super() looks at the next class in the MRO (method resolution order, accessed with cls.__mro__) to call the methods.   

Some material even give a more clear definition as: more clear definition on super()

def  super ( cls , inst ) : 
    mro = inst.__class__.mro( ) 
    return mro[mro.index( cls )  +  1 ]

Create a multiple inheritnce class struture as below:

class state():
    def __init__(self):
        pass

class event():
    def __init__(self):
        pass

class happystate(state,event):
    def __init__(self):
        print(super())
        print(super(happystate,self))

The mro list :

>>> happystate.__mro__
(<class '__main__.happystate'>, <class '__main__.state'>, <class '__main__.event'>, <class 'object'>)

The super() in happystate class will look at the next class in the MRO, it is state class in this status.

x=happystate()
<super: <class 'happystate'>, <happystate object>>
<super: <class 'happystate'>, <happystate object>>

Why super() in happystate class point to itself instead of the next class in MRO list----state?
If super() point to state,the output should be as:

x=happystate()
<super: <class 'state'>, <state object>>
<super: <class 'state'>, <state object>>

Solution

  • It doesn't "point" to the same class, super is a class of its own:

    >>> super
    <class 'super'>
    
    >>> class happystate(state,event):
    ...     def __init__(self):
    ...         print(super(), type(super()))
    ... 
    >>> happystate()
    <super: <class 'happystate'>, <happystate object>> <class 'super'>
    

    So you can also use it outside the class:

    >>> super(happystate)
    <super: <class 'happystate'>, NULL>
    

    But inside the class it has some special behavior, so let's save it as an instance variable so we can interact with it a bit more:

    >>> class happystate(state,event):
    ...     def __init__(self):
    ...         self.s = super()
    ... 
    >>> h = happystate()
    >>> h.s.__self_class__
    <class '__main__.happystate'>
    >>> h.s.__thisclass__
    <class '__main__.happystate'>                    # the class
    >>> h.s.__self__                      
    <__main__.happystate object at 0x7fa8a42642e0>   # the object of the class
    

    But that still doesn't tell us much, so let's jump at the docs for super:

    Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class.

    So at the end of the day super is there to help you access stuff from the parent class(es) without having to traverse the MRO yourself, which means cleaner code. Let's use this newfound knowledge and modify your code a bit:

    class state():
        def __init__(self, x):
            print("state __init__", x)
            self.x = x
    
    class event():
        def __init__(self):
            print("event __init__")
    
    class happystate(state,event):
        def __init__(self):
            super(state, self).__init__()
            self.s = super()
    

    Now let's try using the modified code:

    >>> h = happystate()
    event __init__        # surprise! It doesn't call `state` 
                          # (or its super - `object`) but the
                          # next in the MRO for `happystate`: `event`!
    
    >>> h.s.__init__(3)   # calls state.__init__ (notice that we pass 1 argument)
    state __init__ 3
    >>> h.x               # state.__init__ received `h` as the `self` argument and updated it!
    3