Home > Enterprise >  Constructor call in inheritance
Constructor call in inheritance

Time:08-15

I have a very naïve question. I have the following code in Python:

class A:
    def __init__(self):
        print("constructor A")

class B(A):
    pass

ob = B()

It gives the following output:

constructor A

Similarly in the following code:

class A:
    def __init__(self,name):
        self.name = name
        print(f'{self.name}')
          
class B(A):
    def __init__(self):   
        print('Class B constructor')   
  
ob1 = B() 
ob2 = B('abc')

Shouldn't the output be:

class B constructor
abc

In fact it gives the following error:

TypeError: B.__init__() takes 1 positional argument but 2 were given

Isn't def __init__(self,name) of class A gets inherited in class B and can't we call it using ob2 = B('abc')?

CodePudding user response:

If you want the __init__() in A to be called, you have to do it yourself:

class A:
    def __init__(self, name):
        self.name = name
        print(f'{self.name}')
          
class B(A):
    def __init__(self, name):
        print('Class B constructor')   
        super().__init__(name):
  
ob2 = B('abc')

Output

class B constructor
abc

CodePudding user response:

You are trying to override constructor inside class B. class B inheritances from class A. And class A constructor except name argument. You don't need add init method inside B class here. One of the advantage using OOP is to reduces repetition. You decided to which parameters parent class will have. If you don't use parent class attributes in child class then there is no logic to create parent class. In short, parent class store attributes and methods in common, and if you want, you can add more attributes and method to child classes.

Code snippet will be like this

class A:
  def __init__(self,name):
    self.name = name
    print(f'{self.name}')
class B(A):
  def __init__(self,name):
    super().__init__(name, ...) # here you can add attributes that is specific to B class
    print('Class B constructor')   


ob2 = B('abc')
      

CodePudding user response:

class A:
    def __init__(self, name):
        self.name = name
        print(f'{self.name}')


class B(A):
    def __init__(self, *args):
        if len(args) == 1:
            super(B, self).__init__(name=args[0])
        else:
            print('Class B constructor')


ob1 = B()
ob2 = B('abc')

*args can control how many objects you enter, in that situation we can enter 1 or 0 objects.
if you enter 0 objects in your constructor, it will generate ('Class B constructor'), but if you enter 1 object, with super() method you call the A class constructor and name is equals to object, that is entered in the constructor.

CodePudding user response:

Just to mention, __init__ is not constructor, it's initializer. It initializes the newly created object returned from __new__.

No, it Python attributes(including methods) of the classes are resolved using mro(Method Resolution Order), Whenever Python finds that, it stops going further.

In this example, Python didn't find fn in C, so it checks B, then A, and now it finds it. (If it didn't find it in A, it checks object class which is the last class in every class's MRO. In that case it raises AttributeError because object doesn't have fn either)

class A:
    @staticmethod
    def fn():
        print("fn in A")

class B(A):
    pass

class C(B):
    pass

print(C.mro())
C.fn()

output:

[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
fn in A

If for example B has defined fn, it stops there:

class A:
    @staticmethod
    def fn():
        print("fn in A")

class B(A):
    @staticmethod
    def fn():
        print("fn in B")

class C(B):
    pass

print(C.mro())
C.fn()

output:

[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
fn in B

Same thing happens to __init__.

If you want your code to work, you need to call the A's initializer inside the B's initializer. Note that the signature of the B's __init__ should compatible with the A's __init__. By compatible I mean it should take at least the number of parameters that A's __init__ takes, because you're going to pass those parameters to A.__init__:

class A:
    def __init__(self, name):
        print("Class A initializer")
        self.name = name
        print(f"{self.name}")


class B(A):
    def __init__(self, name):
        super().__init__(name)
        print("Class B initializer")


ob2 = B("abc")

CodePudding user response:

it's because you __init__ take only one argument self which is always passed you need to add name to the definition of B

class A:
    def __init__(self,name):
        self.name = name
        print(f'{self.name}')
      
class B(A):
    def __init__(self, name):   
        print('Class B constructor')   

ob2 = B('abc')
  • Related