Home > Mobile >  Multiple Inheritance --> Fish.__init__() missing 2 required positional arguments
Multiple Inheritance --> Fish.__init__() missing 2 required positional arguments

Time:11-10

I have been practicing multiple inheritance, the example can sound weird but this is what I have in mind...

Here is a main class called Pet and that has two children Cat and Fish and, I wanted to mix the skills of the cat and the skills of the fish in another class but it seems not to work, some idea?

class Pet:
    def __init__(self, name, age):
        self.name = name
        self.age = age


class Cat(Pet):
    def __init__(self, name, age,climb):
        super().__init__(name, age)
        self.mtsClimbed = climb
        
class Fish(Pet):
    def __init__(self, name, age, breathing, living):
        super().__init__(name, age)
        self.breathing = breathing
        self.place_living = living


class fishCat(Cat, Fish):
    def __init__(self, name, age, climb, breathing, living ):
        
        Cat.__init__(self, name, age, climb) 
        Fish.__init__(self, name, age, breathing, living)
        
fishy = fishCat('Garfield', 2, '20 mts' , 'Water', 'Sea')
    

The error I got

line 15, in __init__super().__init__(name, age)
TypeError: Fish.__init__() missing 2 required positional arguments: 'breathing' and 'living'

CodePudding user response:

super() doesn't call the first parent class' method. It calls the next method in the MRO, or method resolution order (link is for Python 2.3, but the same algorithm is still used today).

class Pet:
    pass

class Cat(Pet):
    pass
        
class Fish(Pet):
    pass

class fishCat(Cat, Fish):
    pass

With this inheritance hierarchy, the MRO of fishCat is [fishCat, Cat, Fish, Pet, object]. You can verify this in Python with the mro() method on a class.

>>> fishCat.mro()
[<class '__main__.fishCat'>, <class '__main__.Cat'>, <class '__main__.Fish'>, <class '__main__.Pet'>, <class 'object'>]

That means that, if you have a fishCat object and Cat.__init__ calls super().__init__, then that will actually call Fish.__init__, despite the fact that Cat is not a subclass of Fish.

Generally, if you're expecting to be dealing with complex diamond inheritance situations like this in Python, you should take all arguments as keyword arguments and have your constructor accept (and forward) any keyword arguments it doesn't recognize.

class Pet:
    def __init__(self, name, age, **kwargs):
        self.name = name
        self.age = age


class Cat(Pet):
    def __init__(self, climb, **kwargs):
        super().__init__(**kwargs)
        self.mtsClimbed = climb
        
class Fish(Pet):
    def __init__(self, breathing, living, **kwargs):
        super().__init__(**kwargs)
        self.breathing = breathing
        self.place_living = living


class fishCat(Cat, Fish):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # Any fishCat-specific initialization goes here ...

This does force you to call constructors with keyword arguments only, so fishCat('Garfield', 2, '20 mts', 'Water', 'Sea') must be replaced with fishCat(name='Garfield', age=2, climb='20 mts', breathing='Water', living='Sea'). But given the complex inheritance hierarchy you have, this level of verbosity is probably an improvement.

Side note: It's worth questioning whether the diamond inheritance is really the best solution here. If it's possible for a thing to be a cat and a fish in your domain model, you might consider some other technique than subclassing. For instance, the "pet types" could be an attribute on your class, and an instance of Pet (which would no longer have subclasses in this example) would have a list self.pet_attributes which indicates all of the things it's capable of. From the small sample you've shown here, it seems to me that subclassing may not be the most ideal solution here.

  • Related