1

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()
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Ninad Walanj
  • 103
  • 8

3 Answers3

1

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
0

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

Mandera
  • 2,647
  • 3
  • 21
  • 26
  • What does the metaclass actually do, and why do you think it's a good tool here? – Mad Physicist Dec 18 '21 at 11:05
  • Updated the explanation, I know it's a bit out there but it really makes multiple inheritances much easier to work with – Mandera Dec 18 '21 at 11:13
  • The python system is pretty well designed. I'm pretty sure introducing a bunch of unexpected behavior is not going to make it easier. That being said, downvote removed. – Mad Physicist Dec 18 '21 at 11:16
0

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.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264