-1

Stuck trying to figure out what is causing the error: TypeError: init() takes 4 positional arguments but 5 were given Any help is appreciated.

# Parent class
class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
# Child class
class Labrador(Dog):
    def __init__(self, name, age, playfulness):
        super().__init__(name, age)
        self.playfulness = playfulness
# Child class
class Poodle(Dog):
    def __init__(self, name, age, potty_trained):
        super().__init__(name, age)
        self.potty_trained = potty_trained
# Grandchild class
class Labradoodle(Labrador,Poodle):
    def __init__(self, name, age, playfulness, potty_trained):
        super().__init__(name, age, playfulness, potty_trained)

dogo2 = Labradoodle("Dookie", 2, 'Very playful', 'Yes')

print(dogo2.playfulness, dogo2.potty_trained)
Robin Sage
  • 969
  • 1
  • 8
  • 24
  • 1
    When you do `super().__init__(name, age, playfulness, potty_trained)`, which function do you imagine is getting called? Normally you'd specify which superclass constructor you're calling - e.g. `super(Poodle).__init__(name, age, potty_trained)` and/or `super(Labrador).__init__(name, age, playfulness)`. But neither of those superclass constructors can handle all four arguments. – Green Cloak Guy Jul 04 '21 at 23:01
  • @GreenCloakGuy Ok, makes sense, but what if I want to get one parameter from the Poodle child class and one from the Labrador child class? – Robin Sage Jul 04 '21 at 23:11
  • 4
    @GreenCloakGuy no, that's absolutely not how it works. You would call `super(Labradoodle, self).__init__` pretty much always, or you could use the convenient zero-argument form which is equivalent to that – juanpa.arrivillaga Jul 04 '21 at 23:19
  • 2
    @GreenCloakGuy So, both `super(Labrador, self).__init__(name, age, playfulness)` and `super(Poodle, self).__init__(name, age, potty_trained)` *would end up calling `Dog.__init__`* which would error. If you want to call a *specific* `__init__`, then you just use `Labrador.__init__` or `Poodle.__init__` – juanpa.arrivillaga Jul 04 '21 at 23:22

2 Answers2

2

That is not how multiple inheritance works: you have to follow the MRO to know to which base class you're actually referring to.

In this case super() is calling Labrador's constructor, simply because it's listed first in Labradoodle's definition, so you have to fulfill its requirements (3 parameters)

Moreover, what you're implementing there is a sort of anti-pattern known as the diamond inheritance tree, which is a highly unadvisable practice, as it leads to many inheritance ambiguities. Generally prefer composition over multiple inheritance, at least whenever it's possible.

However, composition doesn't fit too well in this case; I think here the best solution is to derive Labradoodle from Dog too and add both playfulness and potty_trained as instance variables.

Max Shouman
  • 1,333
  • 1
  • 4
  • 11
  • Yeah, I've read about the the diamond inheritance. Although there is a way out of it, it is not advisable. I was just trying to make sense of all these types of inheritance. I came across this website where they use the way I posted here, getting parameters from two Child classes, but I couldn't get it to work. https://www.edureka.co/blog/inheritance-in-python/ – Robin Sage Jul 05 '21 at 01:40
1

The problem here is that you are calling super().__init__ in Labradoodle with more arguments than you should. The __init__ that is being called is the Labrador one, which only contains 3 arguments.

You can read up more on what gets called in these cases here: Calling parent class __init__ with multiple inheritance, what's the right way?

Adam Minas
  • 136
  • 8
  • Ok, I read it, that was helpful. So basically you can't call parameters from differnt child classes? You'd have to specify which child class you want in the super() function? – Robin Sage Jul 04 '21 at 23:15
  • 1
    @RobinSage **no absolutely not** That defeats the entire purpose of super. – juanpa.arrivillaga Jul 04 '21 at 23:21