Search code examples
pythoninheritancesuper

Calling super().__init__() gives the wrong method when it is overridden


I have a base class and a sub class. In the base class I have an instance method which is called in __init__. In the sub class I have overridden this method. The super() call in the sub class does not call the original base method but the overridden sub class method. Why?

class BaseClass:
    def __init__(self, value):

        self.value = value
        self.transformed_value = self.create_value(value)

    def create_value(self, value):

        print("base")
        return value + 1

class SubClass(BaseClass):
    def __init__(self, value):

        super().__init__(value)

    def create_value(self, value):
        print("sub")
        return value + 2

s = SubClass(3)

I expect the print output to be "base", but the actual output is "sub". How can I modify the code to get "base" without explicitly calling BaseClass.create_value(self, value) in the __init__ of BaseClass?


Solution

  • Python is dynamically typed. That means when you write

    def __init__(self, value):
        self.value = value
        self.transformed_value = self.create_value(value)
    

    is doesn't matter which class that method belongs to when evaluating self.create_value: it only matters what the type of self is when the method is called.

    When you call SubClass(3), you eventually call SubClass.__init__, which immediately calls BaseClass.__init__ with an argument of type SubClass. Since SubClass.create_value is defined, that's what self.create_value resolves to.

    If, for whatever reason, you insist the BaseClass.__init__ calls BaseClass.create_value on its self argument, you have to do so explicitly with BaseClass.create_value(self, value). However, that's rarely a good idea. If the type of self wants a method it overrides to be called, that's its responsibility to do so, by using super itself:

    def create_value(self, value):
        rv = super().create_value()
        print("sub")
        return value + 2  # Or perhaps do something with rv first?