Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

python

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?

like image 838
direprobs Avatar asked Sep 15 '16 15:09

direprobs


People also ask

What is __ new __ and __ init __ method Why do we need both these methods?

__new__ method will be called when an object is created and __init__ method will be called to initialize the object. In the base class object , the __new__ method is defined as a static method which requires to pass a parameter cls .

What is __ new __?

__new__ is the first step of instance creation. It's called first, and is responsible for returning a new instance of your class. In contrast, __init__ doesn't return anything; it's only responsible for initializing the instance after it's been created.

Is __ new __ a class method?

__new__ is a static method, not a class method.

What is difference between INIT and New in Python?

__new__ returns a instance of class. __init__ receives the instances of the class returned by __new__ . Use __init__ to initilize value.


1 Answers

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>
like image 99
zvone Avatar answered Oct 18 '22 06:10

zvone