39

In Python 2.7 and 3, I use the following method to call a super-class's function:

class C(B):
    def __init__(self):
        B.__init__(self)

I see it's also possible to replace B.__init__(self) with super(B, self).__init__() and in python3 super().__init__().

Are there any advantages or disadvantages to doing this either way? It makes more sense to call it from B directly for me at least, but maybe there's a good reason where super() can only be used when using metaclasses (which I generally avoid).

martineau
  • 119,623
  • 25
  • 170
  • 301
johannestaas
  • 1,175
  • 1
  • 9
  • 15

1 Answers1

75

For single inheritance, super() is just a fancier way to refer to the base type. That way, you make the code more maintainable, for example in case you want to change the base type’s name. When you are using super everywhere, you just need to change it in the class line.

The real benefit comes with multiple inheritance though. When using super, a single call will not only automatically call the method of all base types (in the correct inheritance order), but it will also make sure that each method is only called once.

This essentially allows types to have a diamond property, e.g. you have a single base type A, and two types B and C which both derive from A. And then you have a type D which inherits from both B and C (making it implicitely inherit from A too—twice). If you call the base types’ methods explicitely now, you will end up calling A’s method twice. But using super, it will only call it once:

class A (object):
    def __init__ (self):
        super().__init__()
        print('A')

class B (A):
    def __init__ (self):
        super().__init__()
        print('B')

class C (A):
    def __init__ (self):
        super().__init__()
        print('C')

class D (C, B):
    def __init__ (self):
        super().__init__()
        print('D')

When we now instantiate D, we get the following output:

>>> D()
A
B
C
D
<__main__.D object at 0x000000000371DD30>

Now, let’s do all that again with manually calling the base type’s method:

class A2 (object):
    def __init__ (self):
        print('A2')

class B2 (A2):
    def __init__ (self):
        A2.__init__(self)
        print('B2')

class C2 (A2):
    def __init__ (self):
        A2.__init__(self)
        print('C2')

class D2 (C2, B2):
    def __init__ (self):
        B2.__init__(self)
        C2.__init__(self)
        print('D2')

And this is the output:

>>> D2()
A2
B2
A2
C2
D2
<__main__.D2 object at 0x0000000003734E48>

As you can see, A2 occurs twice. This is usually not what you want. It gets even messier when you manually call method of one of your base types that uses super. So instead, you should just use super() to make sure everything works, and also so you don’t have to worry about it too much.

Marcos Modenesi
  • 277
  • 1
  • 11
poke
  • 369,085
  • 72
  • 557
  • 602
  • 4
    base type :?. base class, child class etc :) seems more natural – Amit Tripathi Sep 13 '15 at 20:37
  • Great example! I was confused in the ordering, why `A`, `B`, `C`, then `D`. So, I took your example, but created a print statement before & after each `super()` call. My hunch was right: it went `D`, `C`, `B`, and `A` for the before `super()` calls. I don't quite understand this, though. Two questions: (1) Why does `B` get called before `A`? I would think `C`s full "stack" would be resolved first. (2) Why is this acting LIFO (last in, first out) as if it were a recursive algo or generator? Once `A`s `super` gets hit, I would think **all** `super`s would be resolved in order. – Mike Williamson Jun 20 '18 at 02:07
  • Oh, I just figured it out: `D` first calls `C`, and when it "sees" `C`s `super()`, it pauses to resume `D`s other inherited class. Then `D` calls `B`, and when it "sees" `B`s `super()`, it again pauses to go back to `C`s `super()` and unpause it (it was first in order). `C` calls `A`, which again pauses when `super()` is struck. (Why does `A` have `super()`??) Then `B` is unpaused and also calls `A`, which I *believe* is skipped since it's already been called. (So, at this point, shouldn't `'B'` be printed out?) Finally, the call upon `A` from `C` is unpaused and `'A'` is printed out. Yes? – Mike Williamson Jun 20 '18 at 02:18
  • 3
    @MikeWilliamson Your understanding is partially right. The logic that determines the order is not actually evaluated when `super()` is called. What the order is based on is the *Method Resolution Order* (MRO) in Python, which is basically a consistent linearization of the type hierarchy. You can see the MRO of `D` by looking at `D.__mro__` which will basically be `D, C, B, A, object`. Unfortunately, calculating the MRO *correctly* is a bit more complex; it uses the [C3 linearization algorithm](https://en.wikipedia.org/wiki/C3_linearization). But simplified, your model of “seeing” works. – poke Jun 20 '18 at 08:09
  • 2
    @MikeWilliamson What `super()` does though is not calculate the next step but simply tell Python to *continue* within the MRO. That is why all classes that are part of a hierarchy should use `super()`, and that’s why `A` calls `super()`. If you were to add another type `class X(object)`, and made `D` also inherit from that, then you would see that without `A`’s `super()`, `X` wouldn’t be called. Since `X` comes after `A` in the MRO, the `super()` is required to tell Python to continue within the MRO. – poke Jun 20 '18 at 08:12
  • Thanks so much, @poke ! I just tested `__mro__`. But then what I said above must be wrong. The order of resolution **is** like a stack, with FILO (first in, last out) resolution. In other words, `D` calls `C` then `B` then `A` then `object`. ***At this point***, `object` resolves (nothing), then `A` (prints `'A'`), then `B`, etc, peeling back out to `D` in FILO order. Is that right? – Mike Williamson Jun 21 '18 at 02:31
  • 1
    @MikeWilliamson I’m not sure what you mean with that.. The MRO is calculated with that algorithm (which can get really complicated; check the example on Wikipedia). Once the MRO is calculated, the `super()` calls will call the types in that order. And of course, since `super()` are function calls, afterwards it returns in the opposite order; but that’s just the call stack. – poke Jun 21 '18 at 07:56
  • Thanks! Yeah, it was the last bit that you wrote that clarified it. I didn't realize at first that it was "just the call stack". I guess I've been playing around with JS too much lately, and was curious if it worked more like the its event loop, which runs in FIFO order. Seemed to me like maybe FIFO would be appropriate here, but yeah, of course it would just be going on the call stack. – Mike Williamson Jun 21 '18 at 17:20
  • On a related note, what is the difference between super(PARENT_NAME, self).__init__() and super().__init__()? – Piyush Kansal Jul 11 '20 at 18:13
  • 1
    @PiyushKansal Inside a class `C`, `super()` does the same thing as `super(C, self)`. Python 3 introduced this shorcut that you don’t need to specify the parameters for the default cases. In most cases (around 99%) you want to use just `super()`. By passing a different type to it, e.g. `super(ParentType, self)`, you would be skipping types in the MRO, so that’s probably not what you want to do. – poke Jul 11 '20 at 18:23
  • @poke that's exactly what I wanted to do, thanks a lot! – Piyush Kansal Jul 11 '20 at 18:37
  • @poke, there's an important detail which might be highlighted here. Take for an instance a class that inherits from A and B, both have __init__ defined, now if I want to use class A and just use some variable that are defined in class B, I won't be able to do this directly by calling super, because it will run __init__ for only 1 class. Either I change the name of __init__ in 1 of the class and call super twice or not use super – Piyush Kansal Jul 11 '20 at 20:03
  • @PiyushKansal The important thing is that you will need to call `super().__init__()` everywhere; in all classes. E.g. in your example, if `C` inherits from `A` and `B`, then you will need `A` and `B` to also call `super().__init__()` even if they inherit from `object`. All types need to call super in order to have the MRO chain be invoked properly. – poke Jul 11 '20 at 21:22
  • @poke super().__init__() will try to find the __init__ function in the parent classes starting from left to right. I mean to say that if it finds it in the first class itself, it will never inherit the variables defined through the __init__ of second parent. Is that right? – Piyush Kansal Jul 11 '20 at 23:28
  • @PiyushKansal There’s isn’t really some “variable inheritance” in Python. But member initializations within `__init__` can be skipped, yes. – With multiple inheritance, you will need to call `super().__init__()` in _all_ base types in order to get the correct inheritance behavior, but if you do, _all_ parents will be called according to the MRO. – I would suggest you to create a new question if you need further details on this. – poke Jul 13 '20 at 06:05
  • @poke thank you for your time. I'll create a new question if required. – Piyush Kansal Jul 13 '20 at 10:37