Home > Back-end >  Using super() with multiple inheritance
Using super() with multiple inheritance

Time:12-18

The code is quite self-explanatory. I want to call both class A and class B __init__ methods.

Code:

class A(object):
    def __init__(self, name):
        self.name = name

class B(object):
    def __init__(self, age):
        self.age = age

class C(A, B):
    def __init__(self, name, age):
        A.__init__(self, name)
        B.__init__(self, age)


    def display(self):
        print(self.name, self.age)

c = C("xyz", 12)
c.display()

Output:

xyz 12

I want to use super() instead of explicitly stating

A.__init__(self, name)
B.__init__(self, age)

I am unable to find any resource for the same, need help.

The following does not work:

class A(object):
    def __init__(self, name):
        super(A, self).__init__()
        self.name = name

class B(object):
    def __init__(self, age):
        super(B, self).__init__()
        self.age = age

class C(A, B):
    def __init__(self, name, age):
        # A.__init__(self, name)
        # B.__init__(self, age)
        super(C, self).__init__(name, age)

    def display(self):
        print(self.name, self.age)

c = C("xyz", 12)
c.display()

CodePudding user response:

When you create an instance of C, it's better if you pass keyword arguments so that you know which is which. Positional arguments work too but with keyword arguments, you don't have to worry about messing up the order of arguments.

Then since C defers the definition of self.name and self.age to parent classes, you immediately pass all arguments up the heirarchy using super().__init__(**kwargs). The best thing about inheritance is that child classes don't even need to know about the parameters it doesn't handle. The first parent class is A, so self.name is defined there. The next parent class in the method resolution order is B, so from A, you pass the remaining keyword arguments to B, using super().__init__(**kwargs) yet again.

class A(object):
    def __init__(self, name, **kwargs):
        self.name = name
        super().__init__(**kwargs)

class B(object):
    def __init__(self, age, **kwargs):
        self.age = age
        super().__init__(**kwargs)    # this is actually not needed but in case you have other base classes after B, might come in handy

class C(A,B):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def display(self):
        print(self.name, self.age)

c = C(name="xyz",age=12)
c.display()

Output:

xyz 12

CodePudding user response:

A simple solution would be to use kwargs in all your initializers, and pass through the ones you don't care about.

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

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

class C(A, B):
    def __init__(self, name, age, **kwargs):
        super().__init__(name=name, age=age, **kwargs)

    def display(self):
        print(self.name, self.age)

By isolating the arguments you care about from kwargs, you ensure that all kwargs get consumed before you hit object.

CodePudding user response:

I made a metaclass in my public library just for this purpose. Set the metaclass of the first base class.

It makes any inheriters automatically call its' bases __init__s without having to worry about super() calls allowing you to write cleaner code. It's also forgiving as it sets missing args to None.

from generallibrary import AutoInitBases

class A(metaclass=AutoInitBases):
    def __init__(self,name):
        self.name=name

class B:
    def __init__(self,age):
        self.age=age

class C(A,B):
    def __init__(self,name,age):
        pass

    def display(self):
        print(self.name,self.age)

c=C("xyz",12)
c.display()
xyz 12

I don't care much about backward compatibility so make sure to lock the version if you install it. It supports python 3.8 and 3.9

pip install generallibrary==2.9.1

  • Related