4

I have a class called 'Student' that creates a basic student object complete with everything needed to kick out a GPA. I have added two methods that deal with gradepoint and a letter grade respectively to then calculate a GPA. My question is what the best method and placement would be to add error handling in case they entered a letter grade that wasn't acceptable (i.e. G or X). Would I do that in the method itself, or is in the program calling it more appropriate?

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

    def get_name(self):
        return self.name

    def get_hours(self):
        return self.hours

    def get_qpoints(self):
        return self.qpoints

    def gpa(self):
        return self.qpoints / self.hours

    def add_grade(self, grade_point, credit_hours):
        self.qpoints += grade_point * credit_hours
        self.hours += credit_hours

    def add_letter_grade(self, grade_letter, credit_hours):
        letter_grades = {
            'A': 4.0,
            'B': 3.0,
            'C': 2.0,
            'D': 1.0,
            'F': 0.0
        }

        grade_point = letter_grades.get(grade_letter)
        self.qpoints += grade_point * credit_hours
        self.hours += credit_hours

def main():
    new_student = Student('Mike Smith', 0, 0)

while True:
    usr_input = input('Please enter the student course information'
                      ' (grade and credit hours) separated'
                      ' by a comma <q to quit>: ')
    if usr_input == 'q':
        break
    else:
        grade, hours = usr_input.split(',')
        if grade.isalpha():
            new_student.add_letter_grade(grade, float(hours))
        else:
            new_student.add_grade(float(grade), float(hours))

print('{0}\'s final GPA was {1:.2f}'.format(new_student.get_name(), new_student.gpa()))


if __name__ == '__main__':
    main()
dawg
  • 98,345
  • 23
  • 131
  • 206
flybonzai
  • 3,763
  • 11
  • 38
  • 72
  • I'm voting to close this question as off-topic because "my code works, how could I improve it" is a better fit for CodeReview.stackexchange.com – TessellatingHeckler Sep 15 '15 at 17:31
  • 4
    @TessellatingHeckler being a better fit for another site isn't a valid close reason, since migration requires the question to be off-topic here in the first place. Standard "too broad" or "opinion-based" reasons are *a better fit* for close votes ;-) – Mathieu Guindon Sep 15 '15 at 17:35

3 Answers3

3
grade_point = letter_grades.get(grade_letter)

This is where you should have your error be triggered.

I would change that letter_grades.get(grade_letter) into letter_grades[grade_letter]. Let it raise a KeyError, and percolate up to the caller.

Catch the exception in your main loop,

try:
    new_student.add_letter_grade(grade, float(hours))
except KeyError:
    print("That looks like a letter grade, but we don't know what it means. Ignoring it.")

```

Chad Miller
  • 1,435
  • 8
  • 11
2

Classes should be easily extensible. Your class should handle the error, but throw one as well so that whoever utilizes that class in the future knows precisely why their input failed.

In Python this is known as raise an exception. Your class method should "gracefully" fail if an exception is raised internally, and in return raise its own exception. Whoever is utilizing the class is responsible for managing the exception thrown by that class.

Here is another post that relates to the same topic.

Community
  • 1
  • 1
TypeKazt
  • 318
  • 1
  • 13
1

This depends entirely on your usage model. Who writes the calling program? What assumptions exist at this interface?

My default practice is to have the method begin by validating the input arguments. This makes the method shareable to a variety of other applications.

However, the important thing is to make sure that the checking happens on one end or the other. Both is highly preferable to neither.

Does that help at all?

Prune
  • 76,765
  • 14
  • 60
  • 81