1

Here is a simplified code of my main code illustrating the behaviour I obtain.

Suppose I have a main class (MAIN) and two classes (A,B) inheriting from it. This main class has a method which is overwriten by A but not by B, which means that B inherits the method from main.

Then I have a class D which inherits from A and from B, and has a method which calls the aforementioned method. From what I have understood in the way multiple inheritance work, if I define D as class D(A,B) then if A and B have a shared method, calling D.method() will call A.method, and vice-versa (i.e if class D(B,A) then B.method is called. The following code exemplifies this text.


class MAIN(object):
    def __init__(self):
        pass
    def print(self):
        print('HELLO MAIN')

class A(MAIN):
    def __init__(self):
        pass
    def print(self):
        print('HELLO A')

class B(MAIN):
    def __init__(self):
        pass


class C(A,B):
    def __init__(self):
        pass

    def Cprint(self):
        self.print()

c = C()

c.Cprint()


class C(B,A):
    def __init__(self):
        pass

    def Cprint(self):
        self.print()

c = C()

c.Cprint()

However this code always print 'HELLO A', i.e even in the case class C(B,A) I don't get a HELLO MAIN as I would expect. What is happening here? Thanks so much in advance

jdeJuan
  • 145
  • 4
  • 13

2 Answers2

3

The mro is (C, A, B, MAIN) with class C(A, B) and (C, B, A, MAIN) with class C(B, A). In both cases, A is before MAIN. B doesn't define .print, so it doesn't matter.

The method uplooks works like this: (pseudo code)

def find_attribute(obj, name):
    if name in obj.__dict__:
        return obj.__dict__[name]
    mro = type(obj).__mro__
    for cls in mro:
        if name in cls.__dict__:
            return cls.__dict__[name] # (Here a bit more magic for descriptors happens)
    raise AttributeError(name)

For the classes this is what their __dict__ look like:

MAIN.__dict__ = {"print": <method MAIN.print>}
A.__dict__ = {"print": <method A.print>}
B.__dict__ = {}
C.__dict__ = {"Cprint": <method C.Cprint>}

As you can see, B does not have a print defined, so in mro=(C, B, A, MAIN) the first print that does get found is in A.

MegaIng
  • 7,361
  • 1
  • 22
  • 35
  • You can call `C.mro()` to see the mro for class `C`. Note that `C` must be a class, and not a class instance. – Tom Karzes Jul 01 '21 at 10:06
  • @TomKarzes Yes. I decided to no explain that and instead link to the other question. Way better explanation that I don't need to fully duplicate here. – MegaIng Jul 01 '21 at 10:08
  • It's interesting to note that certain inheritance sequences are forbidden. For example, `class C(A, MAIN)` is allowed, but `class C(MAIN, A)` gives an error, since it cannot create a consistent mro. – Tom Karzes Jul 01 '21 at 10:19
  • Thanks for the reply. Just one point. If `B` does not define `.print` then it should inherit MAIN `.print` method, then in the case where the mro is (C, B, A, MAIN), 'HELLO MAIN' should be printed. Am I right? – jdeJuan Jul 01 '21 at 10:22
  • @jdeJuan No. `B` does not have a `.print` attribute/method. I will update the answer and fully explain it. – MegaIng Jul 01 '21 at 10:23
  • 1
    @jdeJuan In the case where the mro is (C, B, A, MAIN), there are two classes that *directly* define `print`: A and MAIN. Since A is before MAIN in the mro, it will use A's `print` method, so you should see `HELLO A`. Figuring out the mro can be tricky (but Python will tell you, as I explained in my first comment). But once you know the mro, it's trivial to determine which method it will use. – Tom Karzes Jul 01 '21 at 10:27
  • thanks for your answers. I thought print will be part of B hence expecting the behaviour I posted. But this provides me a bit more of understanding about how python inheritance works. Didnt expect inherited non-overwritted methods did not take part of the __dict__ – jdeJuan Jul 01 '21 at 11:44
0

You are inheriting the Class A everywhere and class A overrrides Main functions print() thats why you dont get the "HELLO MAIN"

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

    def Cprint(self):
        self.print()

inherit only B class which does not overrides Main class print function then you will get the HELLO MAIN output