2

I get the error: TypeError: __init__() takes exactly 2 arguments (3 given)

When trying to instantiate an object from the class Top:

super(Middle1, self).__init__(name, "middle")

class Base(object):
    def __init__(self, name, type):
        pass

class Middle1(Base):
    def __init__(self, name):
        super(Middle1, self).__init__(name, "middle1")

class Middle2(Base):
    def __init__(self, name):
        super(Middle2, self).__init__(name, "middle2")

class Middle3(Base):
    def __init__(self, name):
        super(Middle3, self).__init__(name, "middle3")

class Top(Middle1, Middle2, Middle3):
    def __init__(self):
        super(Top, self).__init__("top")

# Here is where it produces the error
if __name__ == '__main__':
    Top()

What am I not understanding about this multiple inheritance issue?

Note: this is python 2.7

EDIT

Ok so I tried something that I think works for my case. This is the equivelent end result, I think it's basically forcing depth first by not calling super and calling each individual __init__ instead.

class Base(object):
    def __init__(self, name, type):
        pass

class Middle1(Base):
    def __init__(self, name, type = "middle1"):
        super(Middle1, self).__init__(name, type)

class Middle2(Base):
    def __init__(self, name, type = "middle2"):
        super(Middle2, self).__init__(name, type)

class Middle3(Base):
    def __init__(self, name, type = "middle3"):
        super(Middle3, self).__init__(name, type)

class Top(Middle1, Middle2, Middle3):
    def __init__(self):
        Middle1.__init__(self, "top")
        Middle2.__init__(self, "top")
        Middle3.__init__(self, "top")

# No errors anymore
if __name__ == '__main__':
    Top()
Esser420
  • 780
  • 1
  • 8
  • 19
  • This isn't technically multiple inheritance. Multiple inheritance is when a class inherits from multiple parent classes directly (and is generally not supported in most programming languages). – apokryfos Mar 09 '17 at 17:25
  • 2
    Have you checked that the code file you are executing is the one you are showing here? Because this code does not produce that error ... – dhke Mar 09 '17 at 17:27
  • You were right. I've edited the code to show my actual situation, Now it produces that error. – Esser420 Mar 09 '17 at 17:37
  • I would refer you to http://stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-inheritance this question as well. (Your question should probably be closed as a duplicate of this one, but I think an answer based on your specific code is useful, and I hesitate to use my dupe hammer to close the question unilaterally when I am providing an answer myself.) – chepner Mar 10 '17 at 01:48
  • It's slightly different from the proposed problem. Mine would work if parents all received same arguments. – Esser420 Mar 10 '17 at 15:26
  • since Base doesn't call super() the implementation is actually broken ... not all of your `__init__` will be called. add a print() to each `__init__` to see this. to see why and fix this read [super() and cooperative methods](https://stackoverflow.com/questions/56714419/super-and-changing-the-signature-of-cooperative-methods/56714809#56714809) – Aviad Rozenhek Jun 22 '19 at 11:45

2 Answers2

4

First, you have to look at the method resolution order of Top:

>>> for c in Top.__mro__: print c
...
<class '__main__.Top'>
<class '__main__.Middle1'>
<class '__main__.Middle2'>
<class '__main__.Middle3'>
<class '__main__.Base'>
<type 'object'>

This helps you see which class each call to super represents.

Your mistake is thinking that the call to super(Middle1, self) refers to the (only) base class Base of Middle1. It does not: it refers to the the class following Middle1 in the MRO of self.__class__. Since self.__class__ is Top, the next class in line is Middle2, whose __init__ takes only one argument.

To use super correctly from a method, you need to ensure that the method takes the same arguments in every class, because you cannot predict which class's method will be called by looking at the code itself; it depends entirely on the type of the object that initiates the chain of calls, which might be a class you aren't even aware of yet.

There are two posts I suggest reading:

Together, they give you a good understanding of when super can be used correctly and how to avoid the problem you see here.

(In full disclosure, I haven't read either post recently, so I will refrain from trying to summarize the advice presented in each.)

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Yes that's mostly it, I expected it to go depth first and then left to right. – Esser420 Mar 10 '17 at 14:56
  • taking the same arguments for `__init__` in every class is not enough, since object.__init__ does not take any parameters, so passing arguments to `super.__init__()` is broken. see [Super() and changing the signature of cooperating methods](https://stackoverflow.com/questions/56714419/super-and-changing-the-signature-of-cooperative-methods/56714809#56714809) – Aviad Rozenhek Jun 22 '19 at 11:47
  • The links I included also discuss removing arguments from calls to `super().__init__` (or other upstream methods) to avoid that problem. – chepner Jun 22 '19 at 12:38
0

How exactly are you instantiating Top objects?

Given your code above, the following works fine:

   topObj = Top()
   middleObj = Middle("middle")
   baseObj = Base("base", "type")
krono
  • 21
  • 5
  • I see you edited your example code. Indeed, with multiple inheritance constructor resolution becomes confusing. – krono Mar 09 '17 at 17:51