lobjc
lobjc

Reputation: 2961

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

I have defined a class in a file named Object.py. When I try to inherit from this class in another file, calling the constructor throws an exception:

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

This is my code:

import Object

class Visitor(Object):
    pass

instance = Visitor()  # this line throws the exception

What am I doing wrong?

Upvotes: 211

Views: 227558

Answers (6)

Karl Knechtel
Karl Knechtel

Reputation: 61526

Modules vs classes with basic import syntax

Errors like this most commonly happen because programmers expect Python to work like Java - where source files are expected to include exactly one "public" class named after the file, and import provides access to the class.

Python is not Java. Python does not have access modifier keywords; it allows putting any number of classes (including zero) in a source file; it also allows for things other than classes at the top level of the code; and it does not have any special behaviour for classes that share a name with the source file.

Code like import Object (assuming that the import succeeds) means that Object will be the name of a module, not a class. Python will not give special treatment to any class here even if it has the same name as the module. A hypothetical class Object defined within Object.py will be the Object attribute of the Object module: Object.Object.

Modules are not classes, and are thus not suitable as base classes.

Supposing that the Object module defines an Object class, to use that as a base class, choose one:

  • refer to it by that qualified name:

    import Object
    
    class Visitor(Object.Object):
        pass
    
  • explicitly alias it:

    import Object
    
    my_object = Object.Object
    class Visitor(my_object):
        pass
    
  • use from ... import syntax to assign the class directly to the desired name:

    from Object import Object
    
    class Visitor(Object):
        pass
    

    or, alias it as well, using as:

    from Object import Object as my_object
    
    class Visitor(my_object):
        pass
    

However, be careful with the latter syntax:

from ... import syntax

For imports that look like from x.y import z, there are two possibilities:

  • x is a package, y is a module within that package, and z is some attribute of that module (again, classes are not special; z could be anything, such as an integer) - i.e., a global variable left over from running the code at the top level of the module.

  • x is a package, y is a sub-package of that package, and z is a module in that sub-package.

(Python represents packages as a kind of module, so there is really only one distinction: whether the thing being imported from the specified package/module is also a module, or some component thereof.)

Which result occurs, of course, depends on what the x package's contents are, which in turn could depend on where x was found. Having packages, subpackages, modules and attributes that reuse a common name, greatly increases the potential for confusion. (Unfortunately, the Python standard library does this in a couple of places, notably with datetime).

For example, given x.py and y.py in the same directory, where x.py implements a x class, one might write from x import x in the y.py code, expecting this to mean the class. However, if this "same directory" happens to be named x, and if the parent directory is the path that gets searched for an absolute import, then from x import x would mean the module (created from the x.py source file, imported from the package representing the x folder).

As long as the package root is on the module search path, the problem is easily avoided by using relative imports. Continuing the example, in y.py, from .x import x unambiguously means the class, and from . import x unambiguously means the sibling module. We don't need to reason about the contents of sys.path, or about what the current working directory will be at the time the import runs (in case sys.path includes a relative path), when writing the code; we only need to ensure that the code is run properly (such that the y module gets its __package__ set properly).

Why this specific error message?

I want to add some detail to the claim "So your inheritance is screwy".

The problem occurs because the code attempts to use something as a base class that isn't a type. Recall that in Python, classes are themselves objects, and that every object has some type (i.e., the class of which it is an instance).

When Python processes a call to a normal class definition, it will gather the contents and make a call to the built-in type (not a function, but a call to the constructor of the type class, which is the class that defines the default type of classes - including itself). That will be passed three arguments: the name of the class, as a string (here, 'Visitor'), a tuple of the base classes for the new class (here, (Object,) - i.e., the module, by itself, in a tuple), and a dictionary of attributes to set for the new class (that will include all the methods and class variables defined inside the class body, plus some special things like '__module__' and '__qualname__').

However, the call to type isn't hard-coded. Instead, Python looks for the type of the bases, in order to figure out what metaclass to use (type is only one possible metaclass). By doing this, Python ensures that derived classes have the same metaclass as the base class.

When the example code runs, Python will look up the type of Object, and find that it is a module. Therefore, instead of trying to create a new type, named Visitor, with Object as its base, it will try to create a new module, using the constructor for the module type (not assigned as a builtin by default).

As it happens, that constructor isn't documented (module objects are only supposed to be created by the import system, not directly by the user), but it takes either one or two arguments: the name of the new module (as a string), and optionally a value to use for the docstring (the module's __doc__ attribute) - which incidentally is not type-checked; it is supposed to be either a string or None, but:

>>> import sys
>>> module = type(sys) # expose the type name
>>> example = module('example', [1, 2, 3])
>>> example.__doc__
[1, 2, 3]

(although it will be ignored by help(example) if it is not a string.)

Similarly, if we try to inherit from other things:

>>> class example(0): pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: int() takes at most 2 arguments (3 given)
>>> class example(()): pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: tuple expected at most 1 argument, got 3

If we try an object whose type does accept three arguments for its constructor, the failure is simply pushed back further:

>>> # 'example' is passed as a buffer to decode,
>>> # ('test',) is passed as an encoding,
>>> # and some dictionary is passed as the error-handling policy.
>>> # All of these values are clearly nonsensical.
>>> class example('test'): pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: str() argument 2 must be str, not tuple

Upvotes: 5

Gerardsson
Gerardsson

Reputation: 382

In my case where I had the problem I was referring to a module when I tried extending the class.

import logging
class UserdefinedLogging(logging):

If you look at the Documentation Info, you'll see "logging" displayed as module.

In this specific case I had to simply inherit the logging module to create an extra class for the logging.

Upvotes: -1

Divesh singh
Divesh singh

Reputation: 437

from Object import Object

or

From Class_Name import Class_name

If Object is a .py file.

Upvotes: 9

SunfiShie
SunfiShie

Reputation: 416

Even after @Mickey Perlstein's answer and his 3 hours of detective work, it still took me a few more minutes to apply this to my own mess. In case anyone else is like me and needs a little more help, here's what was going on in my situation.

  • responses is a module
  • Response is a base class within the responses module
  • GeoJsonResponse is a new class derived from Response

Initial GeoJsonResponse class:

from pyexample.responses import Response

class GeoJsonResponse(Response):

    def __init__(self, geo_json_data):

Looks fine. No problems until you try to debug the thing, which is when you get a bunch of seemingly vague error messages like this:

from pyexample.responses import GeoJsonResponse ..\pyexample\responses\GeoJsonResponse.py:12: in (module) class GeoJsonResponse(Response):

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

=================================== ERRORS ====================================

___________________ ERROR collecting tests/test_geojson.py ____________________

test_geojson.py:2: in (module) from pyexample.responses import GeoJsonResponse ..\pyexample\responses \GeoJsonResponse.py:12: in (module)

class GeoJsonResponse(Response): E TypeError: module() takes at most 2 arguments (3 given)

ERROR: not found: \PyExample\tests\test_geojson.py::TestGeoJson::test_api_response

C:\Python37\lib\site-packages\aenum__init__.py:163

(no name 'PyExample\ tests\test_geojson.py::TestGeoJson::test_api_response' in any of [])

The errors were doing their best to point me in the right direction, and @Mickey Perlstein's answer was dead on, it just took me a minute to put it all together in my own context:

I was importing the module:

from pyexample.responses import Response

when I should have been importing the class:

from pyexample.responses.Response import Response

Hope this helps someone. (In my defense, it's still pretty early.)

Upvotes: 20

Sheena
Sheena

Reputation: 16212

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

Upvotes: 331

Anthony Rutledge
Anthony Rutledge

Reputation: 7564

You may also do the following in Python 3.6.1

from Object import Object as Parent

and your class definition to:

class Visitor(Parent):

Upvotes: 3

Related Questions