does __new__ over ride __init__ in python

77 Views Asked by At

I have the following code:

class Demo:
    def __new__(self):
        self.__init__(self)
        print("Demo's __new__() invoked")
    def __init__(self):
        print("Demo's __init__() invoked")
                      
class Derived_Demo(Demo):
    def __new__(self):
        print("Derived_Demo's __new__() invoked")
    def __init__(self):
        print("Derived_Demo's __init__() invoked")

def main():
    obj1 = Derived_Demo()
    obj2 = Demo()

main()

I'm trying to understand the order of execution:

  1. __new__ in the derived class is called first

  2. why doesn't _init_ in the derived class called next?

3

There are 3 best solutions below

0
ShadowRanger On BEST ANSWER

Your code is fundamentally broken, which is why __init__ isn't being invoked as expected.

Per the docs for __new__:

If __new__() does not return an instance of cls, then the new instance’s __init__() method will not be invoked.

Your __new__ doesn't explicitly return anything, so it's returning None.

If you write it correctly (including removing explicit calls to __init__ within __new__, and explicitly delegating up the inheritance chain for Derived_Demo's __new__ and __init__), you code will work as expected:

class Demo:
    def __new__(cls):
        print("Demo's __new__() invoked")
        return super().__new__(cls)
    def __init__(self):
        print("Demo's __init__() invoked")

class Derived_Demo(Demo):
    def __new__(cls):
        print("Derived_Demo's __new__() invoked")
        return super().__new__(cls)
    def __init__(self):
        print("Derived_Demo's __init__() invoked")
        super().__init__()

def main():
    obj1 = Derived_Demo()
    obj2 = Demo()

main()

Try it online!

Which outputs:

Derived_Demo's __new__() invoked
Demo's __new__() invoked
Derived_Demo's __init__() invoked
Demo's __init__() invoked
Demo's __new__() invoked
Demo's __init__() invoked

where super() based dispatch completely runs all the __new__(s) then all the __init__(s) are run on the resulting object produced by __new__.

0
Caspar Wylie On

Here is an execution order that should make better sense given the output you are seeing.

  1. "Derived_Demo's __new__() invoked" - because you instantiate Derived_Demo, and __new__ will always be called first.
  2. Your Derived_Demo.__new__ returns None, meaning before arriving at Derived_Demo.__init__, obj1 is None (put a print(obj1) immediately after it is defined to see).
  3. "Demo's __init__() invoked" - because Demo.__new__ is called first, but you expliclity call __init__ in __new__ (which is an anti-pattern but Python doesn't disallow it).
  4. "Demo's __new__() invoked" - to be expected after __init__ is called.
0
NotAName On

If you went and overriden __new__() correctly then you'd see a very different order of execution:

class Demo:
    def __new__(cls, *args, **kwargs):
        print("Demo's __new__() invoked")
        return super(Demo, cls).__new__(cls, *args, **kwargs)    
    def __init__(self):
        print("Demo's __init__() invoked")
                      
class Derived_Demo(Demo):
    def __init__(self):
        print("Derived_Demo's __init__() invoked")

def main():
    obj1 = Derived_Demo()
    obj2 = Demo()

    # this also creates instances correctly
    print(obj1, obj2)

main()
>>> Demo's __new__() invoked
>>> Derived_Demo's __init__() invoked
>>> Demo's __new__() invoked
>>> Demo's __init__() invoked

>>> <__main__.Derived_Demo object at 0x00000213C0E2BB20> <__main__.Demo object at 0x00000213C0EE5BA0>