Composition in Python

By: Cam Wohlfeil
Published: 2018-12-03 1300 EST
Modified: 2018-12-11 1130 EST
Category: Programming
Tags: python

I'm a big fan of composition over inheritance in OOP, the only issue is that it's a bit harder to wrap your head around than basic inheritance. For a quick reminder, composition is simply aggregating objects together by making objects attributes of other objects. If an object has-a different object, then it is using a composition relationship. If an object is-a parent object, then it using an inheritance relationship.

class Student:
    def __init__(self, name, student_number):
        self.name = name
        self.student_number = student_number
        # A student has-a list of running courses
        self.classes = []

    def enroll(self, course_running):
        self.classes.append(course_running)
        course_running.add_student(self)


class Department:
    def __init__(self, name, department_code):
        self.name = name
        self.department_code = department_code
        # A department has-a list of courses
        self.courses = {}

    def add_course(self, description, course_code, credits):
        self.courses[course_code] = Course(description, course_code, credits, self)
        return self.courses[course_code]


class Course:
    # Theoretical description of a course, i.e. what would be in a course catalog
    def __init__(self, description, course_code, credits, department):
        self.description = description
        self.course_code = course_code
        self.credits = credits
        self.department = department
        self.department.add_course(self)

        # A course has-a list of running courses
        self.runnings = []

    def add_running(self, year):
        self.runnings.append(CourseRunning(self, year))
        return self.runnings[-1]


class CourseRunning:
    # Concrete instance of a course, i.e. what would be in a student's schedule
    def __init__(self, course, year):
        self.course = course
        self.year = year
        # A running course has-a list of students
        self.students = []

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


math_dept = Department("Mathematics and Applied Mathematics", "MAM")
mam1000w = math_dept.add_course("Mathematics 1000", "MAM1000W", 1)
mam1000w_2018 = mam1000w.add_running(2018)

bob = Student("Bob", "Smith")
bob.enroll(mam1000w_2018)

References