-1

I tried to understand multiple inheritance behaviour with Python, so I tried something but I have no idea why the output of my code is what it is. (I know that diamond inheritance is bad, but was interested to see what happens.)

class Base:
    name = "Base"
    def test(self):
        print("I am", self.name)

class A(Base):
    internal_name = "A"
    def __init__(self):
        self.name = "A"
        super().__init__()

    def test(self):
        super().test()
        print("Called from A")

class B(Base):
    internal_name = "B"
    def __init__(self):
        self.name = "B"
        super().__init__()

    def test(self):
        super().test()
        print("Called from B")

class C(A, B):
    def __init__(self):
        self.name = "C"
        A.__init__(self)
        B.__init__(self)

    def identify(self):
        print("Internal name:", self.internal_name)

c = C()
c.test()
c.identify()

The output on running this code:

I am B
Called from B
Called from A
Internal name: A

What I expected to see as output:

C() calls the __init__ of C, thus sets the variable name to "C", and then calls A.__init__, thus I thought it would override self.name with "A", and then call Base.__init__(), not doing anything. Then the same with B, overriding self.name to "B".

I would either expect the output to either be

I am B
Called from A
I am B
Called from B

or just one call, but the part that "I am B" is printed once, but that "Called from" appears twice completely caught me unexpectedly. And then internal_name is "A" for some reason, even though I called B.init() second, and also it's only the second inherited from class. What is happening with that order?

10
  • 4
    How super().__init__() solves the next method to call see this document docs.python.org/3/howto/mro.html#python-2-3-mro . Even though it is for very old Python 2.3 this is still the way the mro (Method Resolution Order) in Python works. Commented Sep 19 at 13:11
  • see also What does "mro()" do? Commented Sep 19 at 13:18
  • When you call A().__init__(), you are creating a new A object and then calling its __init__ method a second time; you are not affecting the current object at all. Commented Sep 19 at 13:21
  • Okay, changed it to A.__init__(self) , that fixed the self.name to be B. But the .test() call still is strange. Currently reading through that mro() stuff, did not know about it, so maybe that resolves it once I am done. Commented Sep 19 at 13:35
  • 1
    Just replace the two calls to A.__init__(self) and B.__init__(self) with one call to super().__init__() Commented Sep 20 at 20:25

1 Answer 1

1

The reason why you are getting that output is due to Python's MRO (Method Resolution Order). In languages that support multiple inheritance, such as Python, MRO plays a crucial role in determining how attributes/methods are inherited and called.

Here, the execution order is, C.test() -> A.test(self) → B.test(self) → Base.test(self)

Calling c.test() triggers the sequence:

  1. A.test (in A via MRO)
  2. Calls super().test() → finds next in MRO after A (B), so it calls B.test
  3. B.test then calls super().test() → next in MRO after B is Base, so it calls Base.test().
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.