Parent method is not called in Parent Class

Question:

class A:
    def __init__(self):
        self.j = 0
        self.calc_i(865)

    def calc_i(self, i):
        self.i = 68 * i


class B(A):
    def __init__(self):
        super().__init__()
        print("i from B is", self.i)

    def calc_i(self, i):
        self.i = 37 * i


b = B()

The Code above should call the parent class method of calc_i according to my understanding because the parent class will run it’s own method and it also doesn’t know where it is being used as a parent.

But When I run it, it calls the child method where i * 37 and not the parent method

Asked By: Abdul Munim

||

Answers:

because the parent class will run it’s own method

No, it won’t.

When B.__init__ calls super().__init__, it’s the same instance of B that gets passed to A.__init__. As a result, the evaluation of self.calc_i uses the method resolution order (MRO) of B, not A, to determine which calc_i method gets called. Because the MRO is [B, A, object], and B defines calc_i, that’s the method invoked by self.calc_i(865).

The most important thing to remember when writing method is that you don’t know the runtime type of self. The only thing you can be reasonably sure of is that type(self) will be a subclass of A, and you can’t predict what methods that subclass will define.

If you absolutely need A.__init__ to call A.calc_i, you need to be explicit:

def __init__(self):
    self.j = 0
    A.calc_i(self, 865)

or, define a method whose name makes it absolutely clear it should not (and cannot without some effort) be overridden.

def __init__(self):
    self.j = 0
    self.__calc_i(self, 865)

def __calc_i(self, i):
    self.i = 68 * i

If you want some uses of calc_i to be overridable, you can define

def calc_i(self, i):
    return self.__calc_i(i)

and use self.__calc_i when it must be A‘s private implemeantion, and self.calc_i where you are OK with a subclass providing its own.

Answered By: chepner