delex
delex

Reputation: 211

__new__ is never run on alternate class

I am having difficulties with invoking the __new__ method.

When creating an instance of A(), I want to return an instance of B() instead if the input below 10. But the instance is created *without running the __new__ or __init__ methods of B, why is that?

class A (object):
    def __init__ (self, IP, s):
        print 'arrived to init a'
        self.IP=IP
        print self.IP
        print s
    def __new__ (cls, IP, s):
        print "arrived to new a"
        if IP>10:
            self = object.__new__ (cls)
            return self  # return n
        else:
            return super (cls, A).__new__ (B)

class B(object):
    def __init__(self,d,s,ar):
        print 'arrived to b'
        self.ip=d
        print self.ip
        print s
        print ar
    def __new__(cls,d,s,ar):
        print 'arrived to new b' 
        self = object.__new__(cls)
        return self  # return n
    def __repr__(self):
        return 'B({}, ..., ...)'.format(getattr(self, 'ip', '<missing>'))

a = A(10 ,"a")
print a

This outputs

arrived to new a
B(<missing>, ..., ...)

instead of

arrived to new a
arrived to new b
arrived to b
10
'a'
?
B(10, ..., ...)

Upvotes: 0

Views: 60

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121654

You are asking object.__new__ to create a new instance here:

return super (cls, A).__new__ (B)

So A.__new__ returns an instance of B() here, and because this returns a new B() instance, and not a new A() instance, B.__init__() will never be called; the types need to match. See the object.__new__ documentation:

If __new__() returns an instance of cls, then the new instance’s __init__() method will be invoked like __init__(self[, ...]), where self is the new instance and the remaining arguments are the same as were passed to __new__().

B() is not an instance of A so __init__ is not called. B.__new__ is not called because you explicitly bypassed it by asking object.__new__ to create the instance instead.

Just call B() directly in your A.__new__:

    if IP>10:
        self = object.__new__ (cls)
        return self  # return n
    else:
        return B(IP, s, 42)

You need to pass in three arguments; I made up 42 for the third.

Because B() is called, B.__new__ will be called. Because B.__new__ returns an instance of B, the __init__ method will also be called.

Demo:

>>> A(10 ,"a")
arrived to new a
arrived to new b
arrived to b
10
a
42
B(10, ..., ...)

Upvotes: 1

Related Questions