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?
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.