Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolving Diamond Inheritance within Python Classes

Consider the following python code:

class Parent(object):
    def __init__(self, name, serial_number):
        self.name = name
        self.serial_number = serial_number


class ChildA(Parent):
    def __init__(self, name, serial_number):
        self.name = name
        self.serial_number = serial_number
        super(ChildA, self).__init__(name = self.name, serial_number = self.serial_number)

    def speak(self):
        print("I am from Child A")


class ChildB(Parent):
    def __init__(self, name, serial_number):
        self.name = name
        self.serial_number = serial_number
        super(ChildB, self).__init__(name = self.name, serial_number = self.serial_number)

    def speak(self):
        print("I am from Child B")


class GrandChild(ChildA, ChildB):
    def __init__(self, a_name, b_name, a_serial_number, b_serial_number):
        self.a_name = a_name
        self.b_name = b_name
        self.a_serial_number = a_serial_number
        self.b_serial_number = b_serial_number
        super(GrandChild, self).__init_( something )

When running the super function in GrandChild, what is the proper way to format the __init__ arguments so that ChildA and ChildB both get the correct arguments?

Also how do you access the two different versions of the speak method (ChildA's version and ChildB's version) from within the GrandChild class?

like image 223
Zoey Avatar asked Sep 13 '17 14:09

Zoey


People also ask

How can we resolve diamond problem in inheritance?

The Diamond Problem is fixed using virtual inheritance, in which the virtual keyword is used when parent classes inherit from a shared grandparent class. By doing so, only one copy of the grandparent class is made, and the object construction of the grandparent class is done by the child class.

How does Python resolve Diamond problems?

How to Solve Diamond Problem in Python? Because of the method resolution order ( __mro__ ) in Python, the ambiguity of the diamond problem in Python becomes irrelevant. The method resolution order in Python is the order in which a method is searched for in the class hierarchy, in case of inheritance.

How does Python resolve multiple inheritance?

Method Resolution Order in Python In the multiple inheritance scenario, any specified attribute is searched first in the current class. If not found, the search continues into parent classes in depth-first, left-right fashion without searching the same class twice.

What type of inheritance will leads to diamond problem?

The "diamond problem" (sometimes referred to as the "Deadly Diamond of Death") is an ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B and C.


2 Answers

so, when you call super from the grandchild, ChildA's __init__ method will be called because super follows the __mro__ property (parents left to right then grandparents left-to-right, then great grandparents, ...)

Since ChildA's init also calls super, then all the super calls will be chained, calling child b's __init__ and eventually the parent init.

For that to work, your interface generally needs to be consistent. That is positional arguments need to mean the same things, and be in the order.

In situations where that's not the case, keyword arguments may work better.

class Parent:    
    def __init__(self, name, serial, **kwargs):
        self.name = name
        self.serial = serial

class ChildA(Parent):    
    def __init__(self, a_name, a_serial, **kwargs):
        self.a_name = a_name
        self.a_serial = a_serial
        super().__init__(**kwargs)

class ChildB(Parent):    
    def __init__(self, b_name, b_serial, **kwargs):
        self.b_name = b_name
        self.b_serial = b_serial
        super().__init__(**kwargs)


class GrandChild(ChildA, ChildB):
    def __init__(self):
        super().__init__(name = "blah", a_name = "a blah", b_name = "b blah", a_serial = 99, b_serial = 99, serial = 30)

Also note that in your code name and serial are reused as instance properties between all the classes and that's probably not what you want.

like image 95
Sam Hartman Avatar answered Sep 19 '22 14:09

Sam Hartman


In python, you can explicitly call a particular method on (one of) your parent class(es):

ChildA.__init__(self, a_name, a_serial)
ChildB.__init__(self, b_name, b_serial)

Note that you need to put the self in explicitly when calling this way.

You can also – as you did – use the super() way, which will call the "first" parent. The exact order is dynamic, but by default it will do left-to-right, depth-first, pre-order scans of your inheritance hierarchy. Hence, your super() call will only call __init__ on ChildA.

like image 35
Niobos Avatar answered Sep 19 '22 14:09

Niobos