Home > database >  Why does super().__init__ () behave differently from super().override() in the Derived class in Pyth
Why does super().__init__ () behave differently from super().override() in the Derived class in Pyth

Time:09-18

I'm wondering how come the self.name is 'abc' when the super().__init__('abc') gets called, but it becomes 'Nicky' when super().override() is called?

Is it because the Derived instance hasn't been created yet during the time super().__init__('abc') is called so the Base class's properties are used instead?

class Base():
    def __init__(self, name):        
        self.name = name
        self.base = 'base class'
        print('Base', self.name)
        
    def override(self):
        print('Base-override', self.name) 

class Derived(Base):
    def __init__(self, name, age):
        super().__init__('abc')
        self.name = name
        self.age = age

    def override(self):
        super().override() 

d = Derived('Nicky', 20)  # Base abc
d.override() # Base-override Nicky

CodePudding user response:

You can understand it better if you add more prints

class Base():
    def __init__(self, name):      
        print('Base1', self.__dict__)  
        self.name = name
        self.base = 'base class'
        print('Base2', self.name, self.__dict__)
        
    def override(self):
        print('Base-override', self.name) 

class Derived(Base):
    def __init__(self, name, age):
        print('Derived1', self.__dict__)
        super().__init__('abc')
        self.name = name
        self.age = age
        print('Derived2', self.name, self.__dict__)

    def override(self):
        super().override() 

d = Derived('Nicky', 20)  # Base abc
d.override() # Base-override Nicky

Output

Derived1 {}
Base1 {}
Base2 abc {'name': 'abc', 'base': 'base class'}
Derived2 Nicky {'name': 'Nicky', 'base': 'base class', 'age': 20}
Base-override Nicky

There are no different versions for the attribute name of your object d, both refers to the same value, don't confuse yourself that name is different between the parent and child class, after all, it's just the same object, regardless of the class hierarchies.

It's just a matter of what you set last at a point in time. Here, when you called super().__init__('abc'), you haven't initialized the name "Nicky" in your Derived.__init__(), thus you saw the value of "abc" first.

CodePudding user response:

It has to do with execution order and how you use the parameters. A class is a collection of data, and has some functions to work on that data. You can use super() to call an implementation explicitly from the parent class.

There is nothing special about __init__ as a method or name as an attribute. Your calling sequence is as follows:

  1. Derived('Nicky', 20) creates a new empty object using object.__new__, then calls Derived.__init__(obj, 'Nicky', 20). Remember that the attribute dictionary is empty at this point.
  2. Derived.__init__(obj, 'Nicky', 20) starts out by calling Base.__init__(obj, 'abc') via super().
  3. Base.__init__(obj, 'abc') sets obj.name to 'abc' and obj.base to 'base class'. It then returns with those two attributes set in the dictionary. This is where the first print happens.
  4. Derived.__init__(obj, 'Nicky', 20) then runs along and sets obj.name to 'Nicky', and obj.age to 20. It then returns. The key point is that obj.name is the same attribute as what Base.__init__ was working on: different methods, same data.
  5. Derived('Nicky', 20) returns the newly created and initialized object, which you assign to the name d.
  6. When you call d.override, or any other method, it has access to d.__dict__ as the previous method left it.

Aside from the lesson on how instance data is stored and passed around, this is a lesson on how to design your initializers. Usually, you call super exactly to avoid doing unnecessary work in the derived class. In __init__, that means letting the base class handle name information. It also means that override as currently implemented shouldn't exist: the base implementation will be called on the derived instance anyway, and if that's all you're doing, you don't actually need to override it:

class Base():
    def __init__(self, name):        
        self.name = name
        self.base = 'base class'
        print('Base', type(self), self.name)
        
    def override(self):
        print('Base-override', type(self), self.name) 

class Derived(Base):
    def __init__(self, name, age):
        super().__init__(name)
        self.age = age

I've added type(self) to your printouts so you can see that it's an instance of Derived that Base methods are called on when you instantiate Derived.

CodePudding user response:

Nothing mysterious here.

You are creating the base class with name = 'abc' but then overriding sef.name with 'Nicky'.

It is clear from this code:

    super().__init__('abc')
    self.name = name
    self.age = age

That in order for self.name = name to be executed, the expression super().__init__('abc') has to be completed.

  • Related