venkat
venkat

Reputation: 11

Understanding diamond problem in inheritance

I'm trying to understand the diamond problem through the following code:

#implement Hybrid Inheritance

class University:
    def __init__(self,univ_name):
        self.univ_name=univ_name
    def show_details(self):
        print(f"University Name : {self.univ_name}")

class Course(University):
    def __init__(self, univ_name,course_name):
        super().__init__(univ_name)
        self.course_name=course_name
    
    def show_details(self):
        super().show_details()
        print(f"Course_name : {self.course_name}")

class Branch(University):
    def __init__(self, univ_name, branch_name):
        super().__init__(univ_name)
        self.branch_name=branch_name

    def show_details(self):
        super().show_details()
        print(f"Branch_name : {self.branch_name}")

class Student(Course,Branch):
    def __init__(self, univ_name, course_name,branch_name):
        Course.__init__(self,univ_name, course_name)
        Branch.__init__(self,univ_name,branch_name)
    def show_details(self):
        Course.show_details(self)
        Branch.show_details(self)

std_1=Student("UTL","Masters","CSE")
std_1.show_details()

When I run the code, I'm getting the following error:

Traceback (most recent call last):
  File "\Assignements\Exercise_4.py", line 35, in <module>
    std_1=Student("UTL","Masters","CSE")
  File "\Assignements\Exercise_4.py", line 29, in __init__
    Course.__init__(self,univ_name, course_name)
  File "\Assignements\Exercise_4.py", line 11, in __init__
    super().__init__(univ_name)
TypeError: __init__() missing 1 required positional argument: 'branch_name'

Is the above error due to the diamond problem, and how can it be resolved?

Upvotes: 1

Views: 57

Answers (1)

Tom McLean
Tom McLean

Reputation: 6327

You can get the __mro__ attribute (meaning method resolution order) to see the class hierarchy:

print(Student.__mro__)
(<class '__main__.Student'>, <class '__main__.Course'>, <class '__main__.Branch'>, <class '__main__.University'>, <class 'object'>)

So in the case of a student, the Course class is calling super().__init__ on the Branch class. This could be fixed by doing like what you did for the Student class and doing:

University.__init__(self, univ_name)

In the Branch and Course classes.

A better way to solve it would be to use composition: A University has courses and branches. Courses have students. Branches have students:

class University:
    def __init__(self, name):
        self.name = name
        self.courses: list[Course] = []
        self.branches: list[Branch] = []

    def add_course(self, course):
        self.courses.append(course)

    def add_branch(self, branch):
        self.branches.append(branch)

    def show_details(self):
        print(f"University Name: {self.name}")
        print("Courses Offered:")
        for course in self.courses:
            course.show_details()
        print("Branches Available:")
        for branch in self.branches:
            branch.show_details()


class Course:
    def __init__(self, name):
        self.name = name
        self.students = []

    def show_details(self):
        print(f"Course Name: {self.name}")
        print("Students: ")
        for student in self.students:
            student.show_details()

    def add_student(self, student):
        self.students.append(student)


class Branch:
    def __init__(self, name):
        self.name = name
        self.students: list[Student] = []

    def show_details(self):
        print(f"Branch Name: {self.name}")
        print("Students: ")
        for student in self.students:
            student.show_details()

    def add_student(self, student):
        self.students.append(student)


class Student:
    def __init__(self, name):
        self.name = name

    def show_details(self):
        print(f"Student Name: {self.name}")


def main():
    university = University("UTL")

    course_masters = Course("Masters")
    branch_cse = Branch("CSE")

    university.add_course(course_masters)
    university.add_branch(branch_cse)

    student = Student("John Doe")
    course_masters.add_student(student)
    branch_cse.add_student(student)
    university.show_details()


if __name__ == "__main__":
    main()

Output:

University Name: UTL
Courses Offered:
Course Name: Masters
Students: 
Student Name: John Doe
Branches Available:
Branch Name: CSE
Students: 
Student Name: John Doe

You could restructure this for your usecase.

Upvotes: 2

Related Questions