6

I'd like to know the type of an instance obtained from super() function. I tried print(super()) and __print(type(super()))__

class Base:
    def __init__(self):
        pass

class Derive(Base):
    def __init__(self):
        print(super())        
        print(type(super()))        
        super().__init__()

d = Derive()

The result is

<super: <class 'Derive'>, <Derive object>>
<class 'super'>

With those result, I was wondering how super().__init__() calls the correct constructor.

Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
wannik
  • 12,212
  • 11
  • 46
  • 58
  • @MarcusMüllerꕺꕺ I knew mro. I'm sorry for being unclear. I changed the title and edit my question to be more specific of what I need. – wannik Aug 14 '15 at 16:18
  • 1
    @MarcusMüllerꕺꕺ: `type(self).__mro__` will vary with subclasses. `__class__.__mro__` will capture the current. – Martijn Pieters Aug 14 '15 at 16:20
  • @AaronHall I edited the titled and the question again. I'm sorry for misdirecting to the base class name. – wannik Aug 14 '15 at 17:31

2 Answers2

4

You can't do what you want with super() directly. Go to the class MRO (see class.__mro__) instead:

class Derive(Base):
    def __init__(self):
        mro = type(self).__mro__
        parent = mro[mro.index(__class__) + 1]
        print(parent)

Here __class__ is the magic closure variable* that references the class the current function was defined in; the above continues to work even when you subclass or mix in additional classes with Derive, even when you produce a diamond inheritance pattern.

Demo:

>>> class Base: pass
... 
>>> class Derive(Base):
...     def __init__(self):
...         mro = type(self).__mro__
...         parent = mro[mro.index(__class__) + 1]
...         print(parent)
... 
>>> Derive()
<class '__main__.Base'>
<__main__.Derive object at 0x10f7476a0>
>>> class Mixin(Base): pass
... 
>>> class Multiple(Derive, Mixin): pass
... 
>>> Multiple()
<class '__main__.Mixin'>
<__main__.Multiple object at 0x10f747ba8>

Note how the Multiple class inherits from both Derive and Mixin, and the next class in the MRO is thus found to be Mixin, not Base, because Mixin also derives from Base.

This copies what super() does; find the next class in the MRO for the instance, relative to the current class.


* For background, see Why is Python 3.x's super() magic?

Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
2

From your comments, you want to know how super knows which method to call next. Super inspects the mro of the instance, knows the current class method it's in, and calls the next one in line. The following demo will work in Python 2 and 3, and in Python 2, it prints the name of each class thanks to the metaclass, so I'll use that output:

First the imports and setup to make the printing nicer:

import inspect

class Meta(type):
    def __repr__(cls):
        return cls.__name__

Next, we define a function to tell us what's going on based on the super object itself

def next_in_line(supobj):
    print('The instance class: {}'.format(supobj.__self_class__))
    print('in this class\'s method: {}'.format(supobj.__thisclass__))
    mro = inspect.getmro(supobj.__self_class__)
    nextindex = mro.index(supobj.__thisclass__) + 1
    print('super will go to {} next'.format(mro[nextindex]))

Finally, we declare a class hierarchy based on the example from the wikipedia entry on C3 linearization, for a sufficiently complex example, note the metaclass repr doesn't work in Python3, but the attribute assignment won't break it. Also note that we use the full super call of super(Name, self) which is equivalent to super() in Python 3, and will still work:

class O(object):
    __metaclass__ = Meta

    def __init__(self):
        next_in_line(super(O, self))
        super(O, self).__init__()

class A(O):
    def __init__(self):
        next_in_line(super(A, self))
        super(A, self).__init__()


class B(O):
    def __init__(self):
        next_in_line(super(B, self))
        super(B, self).__init__()


class C(O):
    def __init__(self):
        next_in_line(super(C, self))
        super(C, self).__init__()


class D(O):
    def __init__(self):
        next_in_line(super(D, self))
        super(D, self).__init__()


class E(O):
    def __init__(self):
        next_in_line(super(E, self))
        super(E, self).__init__()


class K1(A, B, C):
    def __init__(self):
        next_in_line(super(K1, self))
        super(K1, self).__init__()


class K2(D, B, E):
    def __init__(self):
        next_in_line(super(K2, self))
        super(K2, self).__init__()


class K3(D, A):
    def __init__(self):
        next_in_line(super(K3, self))
        super(K3, self).__init__()


class Z(K1, K2, K3):
    def __init__(self):
        next_in_line(super(Z, self))
        super(Z, self).__init__()

Now when we print the mro of Z, we get the method resolution order defined by this algorithm applied to the inheritance tree:

>>> print(inspect.getmro(Z))
(Z, K1, K2, K3, D, A, B, C, E, O, <type 'object'>)

And when we call Z(), because our function uses the mro, we'll visit each method in order:

>>> Z()
The instance class: Z
in this class's method: Z
super will go to K1 next
The instance class: Z
in this class's method: K1
super will go to K2 next
The instance class: Z
in this class's method: K2
super will go to K3 next
The instance class: Z
in this class's method: K3
super will go to D next
The instance class: Z
in this class's method: D
super will go to A next
The instance class: Z
in this class's method: A
super will go to B next
The instance class: Z
in this class's method: B
super will go to C next
The instance class: Z
in this class's method: C
super will go to E next
The instance class: Z
in this class's method: E
super will go to O next
The instance class: Z
in this class's method: O
super will go to <type 'object'> next

And we stop at object.__init__. From the above we can see that super always knows what class of the instance it is in, the class's method that it is currently in, and can deduce from the instance class's MRO where to go next.


I'd like to know the name of the base class?

If you only want the direct base (or more than one, in the case of multiple inheritance), you can use the __bases__ attribute, which returns a tuple

>>> Derive.__bases__
(<class __main__.Base at 0xffeb517c>,)

>>> Derive.__bases__[0].__name__
'Base'

I recommend the inspect module for getting the Method Resolution Order (which super follows based on the original caller's class):

>>> import inspect
>>> inspect.getmro(Derive)
(<class __main__.Derive at 0xffeb51dc>, <class __main__.Base at 0xffeb517c>)

Getting it from super

super().__self_class__ gives the instance class, and super().__thisclass__ gives us the current class. We can use the instance's MRO and look up the class that comes next. I presume you wouldn't do this in the final parent, so I'm not catching an index error:

class Base:
    def __init__(self):
        print(super().__self_class__)
        print(super().__thisclass__)


class Derive(Base):
    def __init__(self):
        print(super().__self_class__)
        print(super().__thisclass__)
        mro = inspect.getmro(super().__self_class__)
        nextindex = mro.index(super().__thisclass__) + 1
        print('super will go to {} next'.format(mro[nextindex]))
        super().__init__()


>>> d = Derive()
<class '__main__.Derive'>
<class '__main__.Derive'>
super will go to <class '__main__.Base'> next
<class '__main__.Derive'>
<class '__main__.Base'>
Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
  • 2
    Note that `super()` can go **sideways** in a multi-inheritance scenario. – Martijn Pieters Aug 14 '15 at 16:11
  • 1
    They may well be *confused* about how `super()` and the MRO work, when it comes to multiple inheritance with a diamond pattern. – Martijn Pieters Aug 14 '15 at 16:20
  • If that's the case, it's an XY problem. I added the question-mark on what appears to be the question as stated, and directly answered it. I'm also voting to close based on that issue. – Russia Must Remove Putin Aug 14 '15 at 16:35
  • @AaronHall Thank you for the answer. However, as MartijnPieters said in his answer, I cannot do it directly with super(). And your solution also require another module to use with super(). Anyway, I think what I don't understand is why super().__init__() call the right constructor even super() is something like , > and type(super()) is . – wannik Aug 14 '15 at 17:14
  • @wannik ok, updating my answer with some more info. – Russia Must Remove Putin Aug 14 '15 at 17:19
  • @AaronHall I think I'm still seeing your old answer. Your last comment is 4 mins ago but the last update of your answer is 26 mins ago. – wannik Aug 14 '15 at 17:25
  • @wannik do you follow my answer? – Russia Must Remove Putin Aug 14 '15 at 18:01
  • @AaronHall Thank you very much for the demonstration. By looking at your and Martijn Pieters's code, I understand that two pieces of information (supobj.__self_class__ and supobj.__thisclass__) or (type(self) and __class__) together with inspect module are required to get the next class in the mro. But I don't know how super() create an instance of the next class so that super().__init__() will call the constructor of the next class. – wannik Aug 14 '15 at 18:43
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/87028/discussion-between-aaron-hall-and-wannik). – Russia Must Remove Putin Aug 14 '15 at 18:44
  • @wannik: `super()` creates an instance of a *special proxy object* that'll search the classes in the `__mro__` sequence for matching methods. No instances of the classes are created. – Martijn Pieters Aug 14 '15 at 19:51