7

from question Why does or rather how does object.__new__ work differently in these two cases

the author wasn't interested in the why, but rather in the how.

I would very much want to understand why, particularly :

  1. why isn't object.__init__ takes no parameters printed instead of object.__new__ (in testclass1)

  2. why no error is raised for testclass3 ? (as it takes no arguments other than self)

code

>>> class testclass1(object):
    ...     pass
    ... 

>>> class testclass2(object):
    ...     def __init__(self,param):
    ...             pass
    ... 

>>> a = object.__new__(testclass1, 56)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: object.__new__() takes no parameters

>>> b = object.__new__(testclass2, 56)

>>> b
    <__main__.testclass2 object at 0x276a5d0>

>>> class testclass3(object):
    ...     def __init__(self):
    ...             pass
    ... 

>>> c = object.__new__(testclass3, 56)

>>> c
    <__main__.testclass3 object at 0x276a790>

>>> c1 = object.__new__(testclass3)

>>> c1
    <__main__.testclass3 object at 0x276a810>
Community
  • 1
  • 1
ychaouche
  • 4,922
  • 2
  • 44
  • 52
  • Did you read [this](http://hg.python.org/cpython/file/44ed0cd3dc6d/Objects/typeobject.c#l2818) comment on the python sources? They decided to *not* raise an error in some circumstances to allow defining only one of `__init__` or `__new__`. Otherwise you'd always have to redefined them, even if they were no-ops. – Bakuriu Oct 09 '13 at 16:34
  • 1
    I don't understand that :s I defined only __init__, it takes no arguments (other than self obviously), I'm passing in an argument to __new__, why doesn't it raise an error ? – ychaouche Oct 09 '13 at 16:40

1 Answers1

18

You are using an older Python version; the error message has since been updated:

>>> object.__new__(testclass1, 56)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object() takes no parameters

Python will only complain about __init__ not supporting arguments if neither __new__ nor __init__ have been overridden; e.g. when you inherit both from object. testclass1 fits that case, testclass3 does not because it has an __init__ method.

This is to support implementing immutable types that don't have a use for __init__ (which would be inherited from object in that case), and mutable types, where __new__ should not care about what arguments __init__ expects (which usually would be more arguments).

See issue 1683368 where Guido van Rossum explains his motivations for this.

The typeobject.c source code has this to say:

You may wonder why object.__new__() only complains about arguments
when object.__init__() is not overridden, and vice versa.

Consider the use cases:

  1. When neither is overridden, we want to hear complaints about excess (i.e., any) arguments, since their presence could indicate there's a bug.

  2. When defining an Immutable type, we are likely to override only __new__(), since __init__() is called too late to initialize an Immutable object. Since __new__() defines the signature for the type, it would be a pain to have to override __init__() just to stop it from complaining about excess arguments.

  3. When defining a Mutable type, we are likely to override only __init__(). So here the converse reasoning applies: we don't want to have to override __new__() just to stop it from complaining.

  4. When __init__() is overridden, and the subclass __init__() calls object.__init__(), the latter should complain about excess arguments; ditto for __new__().

Use cases 2 and 3 make it unattractive to unconditionally check for excess arguments. The best solution that addresses all four use cases is as follows: __init__() complains about excess arguments unless __new__() is overridden and __init__() is not overridden (IOW, if __init__() is overridden or __new__() is not overridden); symmetrically, __new__() complains about excess arguments unless __init__() is overridden and __new__() is not overridden (IOW, if __new__() is overridden or __init__() is not overridden).

However, for backwards compatibility, this breaks too much code. Therefore, in 2.6, we'll warn about excess arguments when both methods are overridden; for all other cases we'll use the above rules.

Note that the .__init__() method itself will still complain! When you create an instance, both __new__ and __init__ are called; your code only calls __new__ directly and does not invoke __init__! Creating an instance of testclass1 and testclass3 both fails if you pass in arguments:

>>> testclass1(56)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object() takes no parameters
>>> testclass3(56)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() takes exactly 1 argument (2 given)

The only difference is that for testclass1 it is the default methods for object() that complain instead a specific error for the custom __init__.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343