TypeError: object.__init__() takes no parameters

At my employer we are in the process of migrating from Python 2.4 to 2.6. When running some existing code under Python 2.6 we started getting DeprecationWarnings about "object.__new__() takes no parameters" and "object.__init__() takes no parameters".

A simple example that triggers the warning:

class MyClass(object):

    def __new__(cls, a, b):
        print 'MyClass.__new__', a, b
        return super(MyClass, cls).__new__(cls, a, b)

    def __init__(self, a, b):
        print 'MyClass.__init__', a, b
        super(MyClass, self).__init__(a, b)

obj = MyClass(6, 7)

This gives:

$ python2.4 simple-warning.py
MyClass.__new__ 6 7
MyClass.__init__ 6 7

$ python2.6 simple-warning.py
MyClass.__new__ 6 7
simple-warning.py:5: DeprecationWarning: object.__new__() takes no parameters
  return super(MyClass, cls).__new__(cls, a, b)
MyClass.__init__ 6 7
simple-warning.py:9: DeprecationWarning: object.__init__() takes no parameters
  super(MyClass, self).__init__(a, b)

It turns out that a change to Python for 2.6 (and 3) means that object.__new__ and object.__init__ no longer take arguments - a TypeError is raised when arguments are passed. To avoid breaking too much pre-existing code, there is a special case that will cause a DeprecationWarning instead of TypeError if both __init__ and __new__ are overridden. This is the case we were running into with our code at work.

The reason for this change seems to make enough sense: object doesn't do anything with arguments to __init__ and __new__ so it shouldn't accept them. Raising an error when arguments are passed highlights code where the code might be doing the wrong thing.

Unfortunately this change also breaks Python's multiple inheritance in a fairly serious way when cooperative super calls are used. Looking at the ticket for this change, this issue was thought about but perhaps the implications were not fully understood. Given that using super with multiple inheritance is common and "correct" practice, it would seem that this change to Python is a step backwards.

Let's look at an even simpler example, one that triggers a TypeError under Python 2.6.

class MyClass(object):

    def __init__(self, a, b):
        print 'MyClass.__init__', a, b
        super(MyClass, self).__init__(a, b)

obj = MyClass(6, 7)

The output looks like:

$ python2.4 simple.py
MyClass.__init__ 6 7

$ python2.6 simple.py
MyClass.__init__ 6 7
Traceback (most recent call last):
  File "simple.py", line 7, in <module>
    obj = MyClass(6, 7)
  File "simple.py", line 5, in __init__
    super(MyClass, self).__init__(a, b)
TypeError: object.__init__() takes no parameters

The fix would seem to be to not pass the arguments to object.__init__:

class MyClass(object):

    def __init__(self, a, b):
        print 'MyClass.__init__', a, b
        super(MyClass, self).__init__()

obj = MyClass(6, 7)

On the surface, this deals with the issue:

$ python2.6 simple-fixed.py
MyClass.__init__ 6 7

But what about when multiple inheritance is brought into the picture?

class MyClass(object):

    def __init__(self, a, b):
        print 'MyClass.__init__', a, b
        super(MyClass, self).__init__()


class AnotherClass(object):

    def __init__(self, a, b):
        print 'AnotherClass.__init__', a, b
        super(AnotherClass, self).__init__()


class MultiClass(MyClass, AnotherClass):

    def __init__(self, a, b):
        print 'MultiClass.__init__', a, b
        super(MultiClass, self).__init__(a, b)

obj = MultiClass(6, 7)

Things don't go so well:

$ python2.6 problem.py
MultiClass.__init__ 6 7
MyClass.__init__ 6 7
Traceback (most recent call last):
  File "problem.py", line 21, in <module>
    obj = MultiClass(6, 7)
  File "problem.py", line 19, in __init__
    super(MultiClass, self).__init__(a, b)
  File "problem.py", line 5, in __init__
    super(MyClass, self).__init__()
TypeError: __init__() takes exactly 3 arguments (1 given)

Not passing the arguments to the super call in MyClass.__init__ means that AnotherClass.__init__ is called with the wrong number of arguments. This can be "fixed" by putting the arguments to the __init__ calls back in but then these classes can no longer be used by themselves.

Another "fix" could be to remove the super call in MyClass.__init__ - after all, object.__init__ doesn't do anything. This avoids the TypeError but also means that AnotherClass.__init__ isn't called at all! This isn't right either.

What is the correct way to deal with this situation in Python 2.6+? Wise developers avoid multiple inheritance where possible but it is sometimes the best solution to a problem. If Python claims to support multiple inheritance then it should do so in a way that the language feature that accompanies it (super) works. This change to the behaviour of object.__init__ and object.__new__ means that super can't really be used with class hierarchies that involves multiple inheritance.

Should this get fixed or am I missing something?

(Thanks to Christian Muirhead for helping with investigations into this issue)

Tags:
posted: Sat, 30 Jan 2010 19:07 | permalink | comments