What happens when you inherent from a module inste

2019-05-22 20:32发布

问题:

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.

回答1:

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)


回答2:

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))