direprobs direprobs - 2 months ago 7
Python Question

What is the downside of using object.__new__ in __new__?

Coding exception classes, I came across this error:

TypeError: object.__new__(A) is not safe, use Exception.__new__()


There's a similar question posted here:
TypeError: object.__new__(int) is not safe, use int.__new__(). So
__new__
was deprecated for the following reason:

[Python-Dev]
__new__
deprecation



Guido van Rossum

"The message means just what it says. :-) There's no point in calling
object.__new__()
with more than a class parameter, and any code that
did so was just dumping those args into a black hole."



But the warning in 3.3 that I get "is not safe" is scary. I try to understand the implication of using
object.__new__
, let's consider this example:

>>> class A(Exception):
... def __new__(cls, *args):
... return object.__new__(A)
...
>>> A()
TypeError: object.__new__(A) is not safe, use Exception.__new__()


Fails miserably. Another Example:

>>> class A(object):
... def __new__(cls, *args):
... return object.__new__(A)
...
>>>
>>> A()
<__main__.A object at 0x0000000002F2E278>


works fine. Although,
object
is a builtin class just like
Exception
with respect to their roles, they share the trait of being builtin-classes. Now with
Exception
, the first example raises
TypeError
, but with
object
, it does not?

(a) What are the downsides of using
object.__new__
that made Python to raise the error (
TypeError:...is not safe...
) in the first Example?


(b) What sort of checking Python performs before to calling
__new__
? Or: What is the condition that makes Python raise the error in the first example?

Answer

There is no problem in calling object.__new__, but there is a problem in not calling Exception.__new__.

Exception class was designed in such way that it is crucial that its __new__ must be called, so it complains if that is not done.

There was a question why this happens only with built-in classes. Python in fact does it with every class which is programmed to do that.

Here is a simplified poor-mans implementation of the same mechanism in a custom class:

class A(object):
    def __new__(cls):
        rtn = object.__new__(cls)
        rtn.new_called = True
        return rtn

    def __init__(self):
        assert getattr(self,'new_called',False), \
            "object.__new__ is unsafe, use A.__new__"

class B(A):
    def __new__(cls):
        return object.__new__(cls)

And now:

>>> A()
<__main__.A object at 0x00000000025CFF98>

>>> B()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __init__
AssertionError: object.__new__ is unsafe, use A.__new__

As a side note, this example from the question actually has two errors:

>>> class A(Exception):
...     def __new__(cls, *args):
...             return object.__new__(A)

The first is that __new__ is called on object, thus ignoring Exception.__new__.

The other, just as severe is that A is passed to __new__ instead of cls, which hinders all classes inherited from A.

See this example:

class A(object):
    def __new__(cls):
        return object.__new__(A)  # The same erroneous line, without Exception

class B(A):
    pass

And now B() does not create an instance of B:

>>> B()
<__main__.A object at 0x00000000025D30B8>