4

Create a son class which has two parents:

class mon():
    def __new__(cls):
        print("i am in mon's new")
        return super().__new__(cls)
    def __init__(self):
        print("i am in mon's init")
        super().__init__()


class far():
    def __new__(cls):
        print("i am in far's new")
        return super().__new__(cls)
    def __init__(self):
        print("i am in far's init")


class son(mon,far):
    def __init__(self):
        super().__init__()

Initialize the son class to check what happened.
super().__init__() in son class will call __init__ method in mon,the __new__ method executed before __init__ in mon,super().__new__(cls) in mon ,it make the code jump into far class,the __new__ method executed before __init__ in far,the code jump back to mon to print i am in mon's init and jump to far to print i am in far's init ,so we get the below output.

son_instance = son()
i am in mon's new
i am in far's new
i am in mon's init
i am in far's init

issue1:

How can rewrite the three class structure to get such output as below when to initialize son class?

son_instance = son()
i am in mon's new
i am in mon's init
i am in far's new
i am in far's init

Delete the statement in far class:

return super().__new__(cls)

The whole three class is as below then:

class mon():
    def __new__(cls):
        print("i am in mon's new")
        return super().__new__(cls)
    def __init__(self):
        print("i am in mon's init")
        super().__init__()


class far():
    def __new__(cls):
        print("i am in far's new")

class son(mon,far):
    def __init__(self):
        super().__init__()

Initialize the son class again.

x=son()
i am in mon's new
i am in far's new

Issue2:
Why the code can't jump back to mon class ?Why can't get the following output?

x=son()
i am in mon's new
i am in far's new
i am in mon's init

If no return super().__new__(cls) in far class,it only take effect on __init__ in far class,but there is no __init__ method in far class at all,why it result in not calling __init__ method in mon class?

2
  • ... first, you know you can comment on the answer, right? (editing the question to add a comment is very confusing) Commented Nov 6, 2021 at 6:05
  • • Also based on what do you claim that "if no return super().__new__(cls) in far class,it only take effect on __init__ in far class"? Surely it's not in the Python specification? Commented Nov 6, 2021 at 6:05

1 Answer 1

6
+50

Let me try to answer your two questions one by one.

How can rewrite the three class structure to get such output as below when to initialize son class?

You can't. Your code needs to create the new instance using the __new__ methods before it can begin to initialize the instance using the __init__ methods. If you want behavior to run in some other order, you'll need to do it somewhere else. You can't adjust the inheritance hierarchy to get it to work.

Why the code can't jump back to mon class ?Why can't get the following output?

Your code isn't working right because far.__new__ is broken (probably because you got slightly overzealous while deleting the __init__ method and deleted one extra line). The __new__ method doesn't return anything, which is equivalent to returning None. If a __new__ method doesn't return an instance of the class that was being created, the __init__ method is never called. If you fix the __new__ method by adding back in return super().__new__(cls), you'll get the output you expect.

That happens even though the far class doesn't have an __init__ method itself. The outer code (type.__call__ in this case) doesn't know the whole inheritance hierarchy and calls each function in turn. It only calls methods on the class that is being created, and it trusts that if it's appropriate, the implementation will call the parent class versions of the function. If son.__new__ (which is inherited) returns None as it does when far.__new__ is broken, then son.__init__ won't get called the way it did when the __new__ methods worked properly.

Here's a very approximate pure Python version of type.call (the actual version is implemented in C):

def __call__(cls, *args):   # this is in a metaclass, so I use cls instead of self
    obj = cls.__new__(*args)
    if isinstance(obj, cls):
        obj.__init__(*args)
    return obj

There's a lot of extra stuff I left out, like how type(obj) lets you get the type of an existing instance, rather than creating a new type. But the behavior when it comes to regular instance creation works as shown.

You can read the full details (in C) in the Python source code repository, if you want. The section where the C version of type.__call__ (called type_call) checks if the object returned from cls.__new__ is of the right type is here.

Sign up to request clarification or add additional context in comments.

4 Comments

,if no return super().__new__(cls) in far class,it only take effect on init in far class,but there is no init method in far class at all,why it result in not calling init method in mon class?
I've updated showing why no __init__ method gets called when __new__ returns None (or any other value that's not an instance of the class that was called). I've included a link directly to the C source where this is implemented.
doesn't new return an instance of the class where init returns None?
@CutePoison: You're misunderstanding the context. Yes, __new__ is supposed to return an instance (and __init__ isn't expected to return anything), but in the questioner's code (the second version), it has a bug where far.__new__ had its return statement removed (probably by mistake?). That's what is causing the __init__ methods to not be run.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.