This is what I have come up with but it seems like there should be a more simple way of programming it.

import string
import math
 
class Student:
    def __init__(self, name, hours, qpoints):
        self.name = name
        self.hours = float(hours)
        self.qpoints = float(qpoints)
    def getName(self):
        return self.name
    def getHours(self):
        return self.hours
    def getQpoints(self):
        return self.qpoints
    def gpa(self):
        return self.qpoints/self.hours
    def addLetterGrade(self, letter, credits):  
        self.hours = credits
        if letter == "A":
            ngrade = 4.0
        elif letter == "A-":
            ngrade = 4.0
        elif letter == "A+":
            ngrade = 4.0
        elif letter == "B":
            ngrade = 3.0
        elif letter == "B-":
            ngrade = 3.0
        elif letter == "B+":
            ngrade = 3.0
        elif letter == "C":
            ngrade = 2.0
        elif letter == "C-":
            ngrade = 2.0
        elif letter == "C+":
            ngrade = 2.0
        elif letter == "D":
            ngrade = 1.0
        elif letter == "D-":
            ngrade = 1.0
        elif letter == "D+":
            ngrade = 1.0
        else:
            ngrade = 0.0
        self.qpoints = credits * ngrade
        print self.qpoints, self.hours  
        
def main():
    print
    print "This program extends the modified student class program by"
    print "implementing an add letter grade method"
    print                                                                                                                 
      
    stu = Student("stu", 0.0, 0.0)
    
    done = False
    while done == False:
        while True:
            grade_str = raw_input("Enter grade (A,A-,A+,B,B-,B+,C,C-,C+,"
                                  "D,D-,D+,F or just "
                                  "Enter=exit loop): ").upper()
            print
            if grade_str == "":
                done = True
                break
            if grade_str not in 'A,A-,A+,B,B-,B+,C,C-,C+,D,D-,D+,F':
                print "Error, use A,A-,A+,B,B-,B+,C,C-,C+,D,D-,D+,F"
            else:
                break
        while done == False:
            credits_str = raw_input("Enter credits (number of hours): ")
            try:
                credits = float(credits_str)
                break
            except ValueError:
                print "Error, use floating point number"
                
        if done == False:
            stu.addLetterGrade(grade_str, credits) 
                   
    
    if stu.getHours() == 0.0:
        print "Zero credit hours recorded"
    else:
        print "Final GPA = ", stu.gpa()
    
    
        
main()

ANY Suggestions?

Your error trapping in the letter grades can be mildly improved:

s = 'A,A-,A+,B,B-,B+,C,C-,C+,D,D-,D+,F'
ps = """
Enter grade letters A through F, you
can also postfix A through D with + or -
(press just Enter to exit the loop when done): """
while True:
    x = raw_input(ps).upper()
    print x  # test
    if x == "":
        done = True
        break
    if x in s:
        break
    else:
        print "Error, use", s

# .........

There is another improvement possible when you assign a value to the grade letters:

letter = 'B+'  # test

# the index found is 0 to 4, and -1 if not found
ngrade = "FDCBA".find(letter[0])  

partial = 0.33  # or 0.25 this may vary with the school
if '+' in letter:
    ngrade = ngrade + partial
if '-' in x:
    ngrade = ngrade - partial
    
print ngrade  # test

I would suggest using a dictionary for the grades, for two reasons:

(1) It'll make your life a lot easier (see code below)

(2) Your code currently contains a bug that will allow a silly or malicious user to get weird results or break the code.

if grade_str not in 'A,A-,A+,B,B-,B+,C,C-,C+,D,D-,D+,F':
   print "Error, use A,A-,A+,B,B-,B+,C,C-,C+,D,D-,D+,F"

Note that if the user enters 'A,A-' or '+' or ',' or something similar, the test will pass even though those aren't valid grades! Use of the dictionary will fix that issue.

Generally, when you have constants like "A", "A-", etc... in your program, the philosophy is to only store the constants once. The reason is that if you later decide to change your constants, you won't have to go hunting through your code to find all the instances of that constant. Instead, you can change them once and be done.

Here's one way to implement that principle:

import string
import math
 
class Student:
    valid_grades = {"A+":4.0, "A":4.0, "A-":4.0, "B+":3.0, "B":3.0, "B-":3.0, \
                    "C+":2.0, "C":2.0, "C-":2.0, "D+":1.0, "D":1.0 ,"D-":1.0, \
                    "F":0.0}
    valid_symbols = valid_grades.keys()
    valid_symbols.sort()
    
    def __init__(self, name, hours, qpoints):
        self.name = name
        self.hours = float(hours)
        self.qpoints = float(qpoints)
    def getName(self):
        return self.name
    def getHours(self):
        return self.hours
    def getQpoints(self):
        return self.qpoints
    def gpa(self):
        return self.qpoints/self.hours
    def addLetterGrade(self, letter, credits):  
        self.hours = credits
        if letter in Student.valid_grades:
            ngrade = Student.valid_grades[letter]
            self.qpoints = credits * ngrade
            print self.qpoints, self.hours  
        else:
            print "Error! invalid grade!"
        
        
def main():
    print
    print "This program extends the modified student class program by"
    print "implementing an add letter grade method"
    print                                                                                                                 
      
    stu = Student("stu", 0.0, 0.0)
    
    done = False
    while done == False:
        while True:
            grade_str = raw_input("Enter grade (" + ','.join(Student.valid_symbols)+\
                                  " or just Enter=exit loop): ").upper()
            print
            if grade_str == "":
                done = True
                break
            if grade_str not in Student.valid_grades:
                print "Error, use "+','.join(Student.valid_symbols)
            else:
                break
        while done == False:
            credits_str = raw_input("Enter credits (number of hours): ")
            try:
                credits = float(credits_str)
                break
            except ValueError:
                print "Error, use floating point number"
                
        if done == False:
            stu.addLetterGrade(grade_str, credits) 
                   
    
    if stu.getHours() == 0.0:
        print "Zero credit hours recorded"
    else:
        print "Final GPA = ", stu.gpa()
    
    
        
main()

The dictionary is now the only place that the grade symbols get defined. If later you wish to change B+ to be 3.33 or 3.25, you can do that with ease and confidence that your code is consistent. Or, if the school decides to allow teachers to award "F+"s, you can add it to the dictionary without having to double-check all your print statements.

And, the endless chain of 'elif's has gone away! :)

You still have one bug that needs attention: it is currently possible for the user to assign negative credit hours to a course.

Hope it helps,
Jeff

Actuall, I never thought about using a dictionary but that makes sense! I will probably not implement this into my program since this is an assignment (and it is not my work) but these answers do help me to see different ways to do things, learn and undertsand. Thanks!

I will work on my negative number problem. Thank you!

This question has already been answered. Start a new discussion instead.