1

The __dict__ method does not produce the same result when the inherited classes are initialized directly instead of using the using super().__init__(). Why?:

class A:
    def __init__(self):
        self.att_a = "A"    

class B:
    def __init__(self):
        self.att_b = "B"

class C(A, B):

    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        self.att_c = "C"

    def get_att(self):
        print(self.__dict__) 

c = C()

c.get_att()

Result: {'attA': 'A', 'attB': 'B', 'attC': 'C'}

Why using super().__init__() does not yield the same result:

class A:
    def __init__(self):
        self.att_a = "A"    

class B:
    def __init__(self):
        self.att_b = "B"

class C(A, B):

    def __init__(self):
        super().__init__() # Modification
        self.att_c = "C"

    def get_att(self):
        print(self.__dict__) 

c = C()

c.get_att()

Result: {'att_a': 'A', 'att_c': 'C'}

Samuel Segal
  • 423
  • 1
  • 7
  • 18
  • See also https://stackoverflow.com/questions/9575409 . Note that this problem is nothing to do with `__dict__`. It's simply to do with what gets called and what doesn't: in your `super()` example, `A.__init__()` gets called and `B.__init__()` doesn't. – jez Apr 29 '20 at 22:40

2 Answers2

2

super().__init__ only calls the next method in the MRO. A.__init__ isn't calling super().__init__, so B.__init__ never gets called.

All classes involved have to cooperate.

class A:
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.att_a = "A"    


class B:
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.att_b = "B"


class C(A, B):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.att_c = "C"

    def get_att(self):
        print(self.__dict__) 


# The MRO of C is [C, A, B, object]
# C.__init__ calls A.__init__
# A.__init__ calls B.__init__
# B.__init__ calls object.__init__
# object, as the root of the object hierarchy,
# does not use super().
c = C()

c.get_att()

The addition of **kwargs to all definitions is to accommodate any class that you may not even be aware of that extends A, B, or C. The class defining __init__ cannot know ahead of time what class super() will invoke next.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • I was about to mark the question as a duplicate but I have to say, none of the previous questions' answers are as clear as this one. – jez Apr 29 '20 at 22:37
  • I usually feel a little guilty about answering questions like this, but in many cases I feel like an answer tailored to the specific question is more useful than a link to a similar question. – chepner Apr 29 '20 at 22:39
0

__init__() like any other method resolved by MRO, ie. calls only one implementation. Multiple inheritance only allows to find the 'right' method definitions in all subtrees: https://docs.python.org/3/library/functions.html#super

Mike
  • 1,107
  • 6
  • 7