Skip to content
Advertisement

Is it possible to create nested class attributes?

I’m pretty new to Python and have recently learned about classes. I’ve been experimenting around with them and have come up with a student/course grading system. Here’s the code so far:

class course:
    TOTAL_COURSES = 0

    def __init__(self, name, room, max_students):
        self.name = name
        self.room = room
        self.max_students = max_students
        self.student_list = []
        course.TOTAL_COURSES += 1
 
    def addStudent(self, student):
        # Checks if the class is below max capacity
        if len(self.student_list) < self.max_students:
            self.student_list.append(student)
            # Adds the course to the student's course list
            student.course_list.append(self)
            return True
        return False

So this creates a course class, which I can add students to and set their rooms and other stuff. I’ve got another class, which is intended to store information on students:

class student:
    TOTAL_STUDENTS = 0

    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
        # Courses in course_list are stored as objects
        self.course_list = []
        student.TOTAL_STUDENTS += 1

Pretty simple; I’ve just been stuck on how to create the actual grading system. If I were to do:

s1 = student("Tom", 17, "Male")
c1 = course("English", "E123", 25)

Would it be possible to then use “nested attributes”? So I’d assign that student’s grade of a course to a value like:

s1.c1.grade = "A+"

This however doesn’t work, and throws an (expected) AttributeError. So would I have to use my previously created course_list?

s1.course_list[0].grade = "A+"

Even then I’m not sure how I’d assign grade to that course object.

Advertisement

Answer

Here’s a solution that addresses some of the above issues by assigning a “course slot” to a student, rather than the course itself. As you might imagine, there is a limit to the number of course slots available which depends on the course max size. The code can be developed a lot further, but I thought this could be good to get you started:

class Student:

    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
        self.courses = {}
        
    def addCourse(self, course):
        if course.status=='Enrolled':
            self.courses[course.name] = course
        else:
            self.courses[course.name] = course.status

            
class Course:

    def __init__(self, name, room, max_students):
        self.name = name
        self.room = room
        self.max_students = max_students
        self.student_list = []
        
        self.course_slots_filled = 0
        self.course_slots_available = max_students
        
    def __str__(self):
        return 'Course_object_{}'.format(self.name)
 
    def check_slots_available(self):
        if self.course_slots_filled < self.max_students:
            return True
        else:
            return False
    
    class CourseSlot:
        def __init__(self, name, student_name, status):
            self.name = name
            self.student_name = student_name
            self.status = status
            self.grade = 'No grade assigned'
        
        def __repr__(self):
            return 'CourseSlot_object_{}'.format(self.name)
        
        def set_grade(self, grade):
            self.grade = grade
            
    
    def assign_course_slot(self, student_name):
        if self.check_slots_available():
            self.course_slots_filled+=1
            self.course_slots_available-=1
            status = 'Enrolled'
            self.student_list.append(student_name)
        else:
            print('Sorry, {} class is full! {} not enrolled.'.format(self.name, student_name))
            status = 'Not enrolled'
            
        return self.CourseSlot(self.name, student_name, status)

Example usage

Instantiate courses:

physics = Course('Physics','101',5)
chemistry = Course('Chemistry','102',1)

Instantiate student 1 and assign a course slot:

s1 = Student("Tom", 17, "Male")
s1.addCourse(physics.assign_course_slot(s1.name))
s1.addCourse(chemistry.assign_course_slot(s1.name))
s1.courses['Physics'].set_grade('A+')

[v for v in s1.courses.values()]

# >>> [CourseSlot_object_Physics, CourseSlot_object_Chemistry]

Instantiate student 2 and assign a course slot

s2 = Student("Susan", 18, "Female")
s2.addCourse(physics.assign_course_slot(s2.name))
s2.addCourse(chemistry.assign_course_slot(s2.name))

#>>> Sorry, Chemistry class is full! Susan not enrolled.

Get course info:

print('Physics course slots filled: ',physics.course_slots_filled)
print('Physics course slots available: ',physics.course_slots_available)
print('Chemistry course slots filled: ',chemistry.course_slots_filled)
print('Chemistry course slots available: ',chemistry.course_slots_available)

#>>> Physics course slots filled:  2
#    Physics course slots available:  3
#    Chemistry course slots filled:  1
#    Chemistry course slots available:  0

print('Physics student list: ',physics.student_list)
print('Chemistry student list: ',chemistry.student_list)

# >>> Physics student list:  ['Tom', 'Susan']
#     Chemistry student list:  ['Tom']

for s in [s1,s2]:
    for c in s.courses.values():
        try:
            print('{} {} grade: {}'.format(s.name, c.name, c.grade))
        except AttributeError:
            pass

# >>> Tom Physics grade: A+
#     Tom Chemistry grade: No grade assigned
#     Susan Physics grade: No grade assigned

I guess the cheat is that the course student_list only gets the name of the student and not the Student object, which could probably work if you pass it a unique ID and then iterate through a list of Student objects to match on ID. Something to think about anyway.

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement