Home > database >  Class inheritance via super with two arguments
Class inheritance via super with two arguments

Time:01-07

In the below code, i replaced args with numbers to demonstrate what classes are inherited.

class Animal:
  def __init__(self, animalName):
    print(animalName, 'is an animal.');

class Mammal(Animal):
  def __init__(self, mammalName):
    print(mammalName, 'is a mammal.')
    super().__init__(mammalName)

class CannotFly(Mammal):
  def __init__(self, mammalThatCantFly):
    print('2', "cannot fly.")
    super().__init__('2')

class CannotSwim(Mammal):
  def __init__(self, mammalThatCantSwim):
    print('1', "cannot swim.")
    super().__init__('1')

# Cat inherits CannotSwim and CannotFly
class Cat(CannotSwim, CannotFly):
  def __init__(self):
    print('I am a cat.');
    super().__init__('Cat')

cat = Cat()

returns

I am a cat.
1 cannot swim.
2 cannot fly.
2 is a mammal.
2 is an animal.

Why is it not the below?

I am a cat.
1 cannot swim.
1 is a mammal.
1 is an animal.
2 cannot fly.
2 is a mammal.
2 is an animal.

There are effectively two call streams, no?

CodePudding user response:

You can see the method resolution order (MRO) for Cat:

>>> Cat.mro()
[<class '__main__.Cat'>, <class '__main__.CannotSwim'>, <class '__main__.CannotFly'>, <class '__main__.Mammal'>, <class '__main__.Animal'>, <class 'object'>]

Each class appears once in the MRO, due to the C3 linearization algorithm. Very briefly, this constructs the MRO from the inheritance graph using a few simple rules:

  1. Each class in the graph appears once.
  2. Each class precedes any of its parent classes.
  3. When a class has two parents, the left-to-right order of the parents is preserved.

("Linearization", because it produces a linear ordering of the nodes in the inheritance graph.)

super() is misnamed; a better name would have been something lie nextclass, because it does not use the current class's list of parents, but the MRO of the self argument. When you call Cat, you are seeing the following calls.

  1. Cat.__init__
  2. Cat.__init__ uses super to call CannotSwim.__init__
  3. CannotSwim.__init__ uses super to call CannotFly.__init__
  4. CannotFly.__init__ uses super to call Mammal.__init__
  5. Mammal.__init__ uses super to call Animal.__init__
  6. Animal.__init__ uses super to call object.__init__

object.__init__ does not use super (it "owns" __init__), so the chain ends there.

In particular, note #3: CannotSwim causes its "sibling" in the inheritance graph to be used, not its own parent.

CodePudding user response:

Take a look at this post What does 'super' do in Python? - difference between super().__init__() and explicit superclass __init__()

Now what it says is that __init__ is called for every class in the instance's mro. you can print that by doing print(Cat.__mro__) this will print out

(<class '__main__.Cat'>, <class '__main__.CannotSwim'>, <class '__main__.CannotFly'>, <class '__main__.Mammal'>, <class '__main__.Animal'>, <class 'object'>)

As you can see there is the order of the calls. Now, why '2' is used and not '1', see the answer in the comments by hussic

  •  Tags:  
  • Related