2

I recently came across this question.

import Object

class Visitor(Object):

    def __init__(self):
        super(Visitor,self).__init__()
    def visit(self, obj):
        pass
    def getIsDone(self):
        return False
    isDone = property(fget =lambda self:self.getIsDone())

I get this error:

TypeError: module.__init__() takes at most 2 arguments (3 given)

and its answer:

class A:pass
print(A)              #outputs <class '__main__.A'>
import urllib
print(urllib)         #outputs <module 'urllib' from '/usr/lib/python3.2/urllib/__init__.py'>

Your error is happening because Object is a module, not a class. So your inheritance is screwy.

Change your import statement to:

from Object import ClassName

and your class definition to:

class Visitor(ClassName):

or

change your class definition to:

class Visitor(Object.ClassName):
   etc

I'm not really satisfied with this answer as I'm not really sure how I get from that error message to the conclusion that I am accidentally inheriting from a module instead of a class. I was wondering if somebody could elaborate on why this error is occurring and what exactly the arguments being given are? When the python interpreter comes across code like this: class Employee(Person) what is happening? What exactly does the answerer mean by my inheritance is screwy? Thanks for any explanations or references to resources.

Community
  • 1
  • 1
user3282276
  • 2,977
  • 5
  • 23
  • 42
  • 1
    python language is so dynamic you can pass any entity that exists. It's only when executed that problems are reported. Had you passed an integer variable as parent, it would have tried to call init() for the int too. – Jean-François Fabre Jun 17 '16 at 08:19
  • 1
    For an overview, see [_The Python Tutorial_](https://docs.python.org/3/tutorial/), [9.5. Inheritance](https://docs.python.org/3/tutorial/classes.html#inheritance). Use of the `import` statement is covered in [6. Modules](https://docs.python.org/3/tutorial/modules.html). – Kevin J. Chase Jun 17 '16 at 08:53

2 Answers2

3

If you put an object called BaseClass in the inheritance list, then the interpreter will call this internally:

type(BaseClass).__init__(cls, name_of_subclass, (BaseClass,), dict_of_subclass)
# or simpler
type(BaseClass)(name_of_subclass, (BaseClass,), dict_of_subclass)

You can create a dummy BaseClass to test it

class Meta(object):
   def __init__(self, name,   base,  subcls):
      print (self, name,   base,  subcls)

Base = Meta('','','')

class Test(Base):
    prop1="hello"

which outputs:

<__main__.Meta object at 0x7f7471666bd0>
<__main__.Meta object at 0x7f7471666c50> Test (<__main__.Meta object at 0x7f7471666bd0>,) {'__module__': '__main__', 'prop1': 'hello'}

To answer your question: When the interpreter sees class Employee(Person): pass, the following will happen:

type(Person).__init__(cls, 'Employee', (Person,), {'__module__': '__main__'})

If Person is a normal class, type(person) will return type itself. Then type.__init__ will get called.

If Person is a module, type(person) will return the object module, which has a method __init__. But this method only takes 2 argument, there you get an error.

import sys
type(sys).__init__(sys,2,3,4)
#Traceback (most recent call last):
#  File "testClassInheritance.py", line 11, in <module>
#    type(sys).__init__(sys,2,3,4)
#TypeError: module.__init__() takes at most 2 arguments (3 given)
gdlmx
  • 5,666
  • 1
  • 17
  • 36
0

To find out where you've gone wrong, you don't really need to look at the error message in this case, it's pretty clear from the code itself.

import foo

Always means that foo is a module (as opposed to from foo import bar where bar can be a module, class, function a variable and so on). This is where naming conventions help us, if PEP8 is followed, then one can easily differentiate between classes and module. The code in your question doesn't follow which is clearly not good for others to understand.

Once you get that you've tried to subclass/inherit a module the rest is not that tricky.

Most of the modules don't define __init__ which means when you try to access it, it simply tries to find it up in the inheritance chain (if you're really interested in that, you can read upon python inheritance, mro etc, but I suspect that's not what has confused you here.) and finds the builtin class module which (do import Object;type(Object) or import os; type(os)) has a __init__ expecting different arguments than your overridden method. This would have been more tricky to debug if your method has exactly same number of arguments as the above.

The error message does not seem helpful to you because there is no way for python to understand whether you intentionally want to override the module.__init__ or some class's __init__. Try

import os
help(type(os))
0xc0de
  • 7,055
  • 4
  • 42
  • 70