Feb-13-2019, 04:19 PM
Hello,
I am not sure to fully understand multiple inheritance with Python 3. I can implement what I want to implement. It works. However, I am not sure to do things as they should be done.
My questions have to do with the constructor (namely "__new__") and the initializer (namely "__init__").
Below, I implement a (very simple) use case :
The simplified code (that shows the implementation skeleton) is :
Please note that the code below is, in essence, identical to the above one. The only difference between the two is that the latter produces outputs.
That is:
Question 1
I have tried to pass parameters to both constructors (Parent1.__new__ and Parent2.__new__). However, I can only pass parameters to one constructor. The constructor which receives the parameters is the one for the parent class that appears first in the declaration of the child class. That is:
If:
If:
I am OK with that. I understand that this is due to the "Method Resolution Order".
However:
Please note that I don't see any reason to pass parameters to the constructors since the object initialisation is done elsewhere (in the method __init__). However, if you can pass parameters to one constructor, then why shouldn't you be able to pass parameters to both constructors ?
Question 2
In order to initialise the parent classes, I use the code below:
I tried:
I think that when I write:
Since "self" is an instance of "Parent1". Then, the returned object should be an instance of "Parent1" bound to the current object "self".
Could you explain why the use of "super" does not work ?
Thanks a lot !
Denis
I am not sure to fully understand multiple inheritance with Python 3. I can implement what I want to implement. It works. However, I am not sure to do things as they should be done.
My questions have to do with the constructor (namely "__new__") and the initializer (namely "__init__").
Below, I implement a (very simple) use case :
![[Image: inheritance-multiple.png]](https://raw.githubusercontent.com/denis-beurive/images/master/images/inheritance-multiple.png)
The simplified code (that shows the implementation skeleton) is :
class Parent1: def __new__(cls, *args, **kwargs): return super(__class__, cls).__new__(cls) def __init__(self, *args, **kwargs): pass class Parent2: def __new__(cls, *args, **kwargs): return super(__class__, cls).__new__(cls) def __init__(self, *args, **kwargs): pass class Child(Parent1, Parent2): def __new__(cls, *args, **kwargs): return super(__class__, cls).__new__(cls, *args, **kwargs) def __init__(self, *args, **kwargs): Parent1.__init__(self, *args, **kwargs) Parent2.__init__(self, *args, **kwargs) c = Child(1, 2, 3, name='C')And I also give a slightly "richer" implementation, which contains code that dumps some interesting information.
Please note that the code below is, in essence, identical to the above one. The only difference between the two is that the latter produces outputs.
from pprint import pformat class Parent1: def __new__(cls, *args, **kwargs): print("%s: Info:\n\t* __class__ = %s\n\t* cls = %s" %(__class__.__name__, __class__.__name__, pformat(cls))) print("%s: new:\n\t* %s%s%s" %(__class__.__name__, cls.__name__, (",\n\t* " if len(args) > 0 else '') + ', '.join(map(lambda x: '"%s"'%str(x), list(args))), (",\n\t* " if len(kwargs) > 0 else '') + ', '.join(map(lambda x: '%s="%s"'%(str(x[0]), str(x[1])), kwargs.items())))) o = super(__class__, cls).__new__(cls) # super(type, type2) -> bound super object; requires issubclass(type2, type) print("%s: cls(that is: %s) is subclass of %s ? %s" %(__class__.__name__, cls.__name__, __class__.__name__, "true" if issubclass(cls, __class__) else "false")) print('%s: o = %s' %(__class__.__name__, pformat(o))) return o def __init__(self, *args, **kwargs): print('%s: self = %s' %(__class__.__name__, self)) print('%s: init(%s%s)' %(__class__.__name__, ', '.join(map(lambda x: '"%s"'%str(x), list(args))), (', ' if len(kwargs) > 0 else '') + ', '.join(map(lambda x: '%s="%s"'%(str(x[0]), str(x[1])), kwargs.items())))) class Parent2: def __new__(cls, *args, **kwargs): print("%s: Info:\n\t* __class__ = %s\n\t* cls = %s" %(__class__.__name__, __class__.__name__, pformat(cls))) print("%s: new:\n\t* %s%s%s" %(__class__.__name__, cls.__name__, (",\n\t* " if len(args) > 0 else '') + ', '.join(map(lambda x: '"%s"'%str(x), list(args))), (",\n\t* " if len(kwargs) > 0 else '') + ', '.join(map(lambda x: '%s="%s"'%(str(x[0]), str(x[1])), kwargs.items())))) o = super(__class__, cls).__new__(cls) # super(type, type2) -> bound super object; requires issubclass(type2, type) print("%s: cls(that is: %s) is subclass of %s ? %s" %(__class__.__name__, cls.__name__, __class__.__name__, "true" if issubclass(cls, __class__) else "false")) print('%s: o = %s' %(__class__.__name__, pformat(o))) return o def __init__(self, *args, **kwargs): print('%s: self = %s' % (__class__.__name__, self)) print('%s: init(%s%s)' %(__class__.__name__, ', '.join(map(lambda x: '"%s"'%str(x), list(args))), (', ' if len(kwargs) > 0 else '') + ', '.join(map(lambda x: '%s="%s"'%(str(x[0]), str(x[1])), kwargs.items())))) class Child(Parent1, Parent2): def __new__(cls, *args, **kwargs): print("%s: Info:\n\t* __class__ = %s\n\t* cls = %s" %(__class__.__name__, __class__.__name__, pformat(cls))) print("%s: new:\n\t* %s%s%s" %(__class__.__name__, cls.__name__, (",\n\t* " if len(args) > 0 else '') + ', '.join(map(lambda x: '"%s"'%str(x), list(args))), (",\n\t* " if len(kwargs) > 0 else '') + ', '.join(map(lambda x: '%s="%s"'%(str(x[0]), str(x[1])), kwargs.items())))) o = super(__class__, cls).__new__(cls, *args, **kwargs) # o = Top1.__new__(cls, *args, **kwargs) # super(type, type2) -> bound super object; requires issubclass(type2, type) print("%s: cls(that is: %s) is subclass of %s ? %s" %(__class__.__name__, cls.__name__, __class__.__name__, "true" if issubclass(cls, __class__) else "false")) print('%s: o = %s' %(__class__.__name__, pformat(o))) return o def __init__(self, *args, **kwargs): print('%s: self = %s' %(__class__.__name__, self)) print('%s: init(%s%s)' %(__class__.__name__, ', '.join(map(lambda x: f'"%s"'%str(x), list(args))), (', ' if len(kwargs) > 0 else '') + ', '.join(map(lambda x: '%s="%s"'%(str(x[0]), str(x[1])), kwargs.items())))) # This is OK: Parent1.__init__(self, *args, **kwargs) Parent2.__init__(self, *args, **kwargs) c = Child(1, 2, 3, name='C')The output is:
Output:$ python3 inheritance-multiple.py
Child: Info:
* __class__ = Child
* cls = <class '__main__.Child'>
Child: new:
* Child,
* "1", "2", "3",
* name="C"
Parent1: Info:
* __class__ = Parent1
* cls = <class '__main__.Child'>
Parent1: new:
* Child,
* "1", "2", "3",
* name="C"
Parent2: Info:
* __class__ = Parent2
* cls = <class '__main__.Child'>
Parent2: new:
* Child
Parent2: cls(that is: Child) is subclass of Parent2 ? true
Parent2: o = <__main__.Child object at 0x7f5fc94b4550>
Parent1: cls(that is: Child) is subclass of Parent1 ? true
Parent1: o = <__main__.Child object at 0x7f5fc94b4550>
Child: cls(that is: Child) is subclass of Child ? true
Child: o = <__main__.Child object at 0x7f5fc94b4550>
Child: self = <__main__.Child object at 0x7f5fc94b4550>
Child: init("1", "2", "3", name="C")
Parent1: self = <__main__.Child object at 0x7f5fc94b4550>
Parent1: init("1", "2", "3", name="C")
Parent2: self = <__main__.Child object at 0x7f5fc94b4550>
Parent2: init("1", "2", "3", name="C")
Focusing on the constructors (__new__), you can see that:- Child.__new__ starts its execution.
- Parent1.__new__ starts its execution.
- Parent2.__new__ executes completely.
- Parent1.__new__ terminates its execution.
- Child.__new__ terminates its execution.
That is:
![[Image: inheritance-multiple-sequence.png]](https://raw.githubusercontent.com/denis-beurive/images/master/images/inheritance-multiple-sequence.png)
Question 1
I have tried to pass parameters to both constructors (Parent1.__new__ and Parent2.__new__). However, I can only pass parameters to one constructor. The constructor which receives the parameters is the one for the parent class that appears first in the declaration of the child class. That is:
If:
class Child(Parent1, Parent2): passThen, the contructor that receives the parameters is Parent1.__new__.
If:
class Child(Parent2, Parent1): passThen, the contructor that receives the parameters is Parent2.__new__.
I am OK with that. I understand that this is due to the "Method Resolution Order".
However:
- why does Parent1.__new__ triggers the execution of __Parent2.new__ ? This seems weird.
- how do I do if I want both constructor to receive parameters ?
Please note that I don't see any reason to pass parameters to the constructors since the object initialisation is done elsewhere (in the method __init__). However, if you can pass parameters to one constructor, then why shouldn't you be able to pass parameters to both constructors ?
Question 2
In order to initialise the parent classes, I use the code below:
Parent1.__init__(self, *args, **kwargs) Parent2.__init__(self, *args, **kwargs)This works. But I wondered: what if I want to use "super" instead ?
I tried:
super(Parent1, self).__init__(*args, **kwargs) super(Parent2, self).__init__(*args, **kwargs)But this strategy does not work. It throws an error.
Error:Traceback (most recent call last):
File "inheritance-multiple.py", line 94, in <module>
c = Child(1, 2, 3, name='C')
File "inheritance-multiple.py", line 89, in __init__
super(Parent2, self).__init__(*args, **kwargs)
TypeError: object.__init__() takes no parameters
This seems weird since, the documentation for the function "super" says:Quote:super() -> same as super(__class__, <first argument>)
super(type) -> unbound super object
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type, type2) -> bound super object; requires issubclass(type2, type)
I think that when I write:
super(Parent1, self).__init__(*args, **kwargs)The third use case applies:
Quote:super(type, obj) -> bound super object; requires isinstance(obj, type)
Since "self" is an instance of "Parent1". Then, the returned object should be an instance of "Parent1" bound to the current object "self".
Could you explain why the use of "super" does not work ?
Thanks a lot !
Denis