For loops in Python

I have the following assignment:
A GPA, or Grade point Average, is calculated by summing the grade points earned in a student’s courses and then dividing by the total units. The grade points for an individual course are calculated by multiplying the units for that course by the appropriate factor depending upon the grade received:

            A receives 4 grade points

            B receives 3 grade points

            C receives 2 grade points

            D receives 1 grade point

            F receives 0 grade point

Your program will have a for loop to calculate multiple GPAs and a for loop to collect individual grades (i.e. a nested for loop).

Any ideas about how to approach this? It seems to me that there has to also be a while loop involved, since we don't know how many courses the user needs to input. Is that true? Is there a way to implement something like a sentinel using only for loops?
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

You very rarely need the while loop in Python. The solution depends on how do you capture the student/course grades. It can be a matrix like that:
grade_matrix = [     # 7 courses
    ['A', 'A', 'A', 'C', 'F', 'B', 'D'],  # student 1
    ['A', 'C', 'A', 'C', 'D', 'B', 'A'],  # student 2
    ['B', 'D', 'B', 'D', 'A', 'A', 'B'],  # student 3
    ['A', 'F', 'C', 'A', 'A', 'A', 'B'],  # student 4
    ['B', 'A', 'A', 'B', 'A', 'A', 'B'],  # student 5
    ['C', 'A', 'B', 'D', 'A', 'B', 'C'],  # student 6
    ['F', 'A', 'C', 'F', 'B', 'C', 'C'],  # student 7
    ['A', 'C', 'A', 'C', 'D', 'C', 'A'],  # student 8
    ['B', 'B', 'F', 'A', 'A', 'A', 'B'],  # student 9
    ['C', 'F', 'F', 'B', 'B', 'B', 'A'],  # student 10
    ['F', 'D', 'D', 'C', 'A', 'F', 'F'],  # student 11

Open in new window

which is a list of lists. When looping through the container or whatever iterable structure, the for loop knows when the iterator has no more values. This is your internal sentinel. The iterated structure signals to the for loop the end of iteration via the exception StopIteration. It is processed internally by the for loop.

You need the while loop only when a logical condition (boolean expression) is used for decision continue/stop. This is not the case because it is clear how many students you have and what courses the student attended.

As you need to calculate the course GPA, the matrix must contain the same number of values/courses for each student. If the student did not attend the course, it must be marked using some special value.

For the above matrix, my solution calculates the GPAs for students:

Open in new window

and the GPAs for courses

Open in new window

(But I did not checked it. I may have error in the solution.)

But the task probably is to calculate only the student grades, which is quite straightforwared. In the case, the matrix need not to be regular (I mean, each student may have different number of marks).

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Frankly, I was expecting further questions. Were you able to write the program? Anyway, thanks for the points ;)
drewmoreAuthor Commented:
You're welcome, but I actually didn't mean to give you the points! haha, I was trying to respond to you and accidentally accepted your answer (new to this site..) nonetheless, I appreciate both your answer and your follow up.

I actually ended up going about it in a completely different way (before reading your response). This is what I wrote:
todo = int(input("How many GPA's would you like to calculate? "))

for x in range (1, todo+1):
    n = int(input("How many courses will you input? "))
    totpoints = 0
    totunits = 0

    for x in range(1, n+1):

        grade = input("Enter grade for course: " )
        if grade == 'A':
            grade = int(4)
        if grade == 'B':
            grade = int(3)
        if grade == 'C':
            grade = int(2)
        if grade == 'D':
            grade = int(1)
        if grade == 'F':
            grade = int(0)
        units = int(input("How many units was the course? "))
        totunits += units
        points = grade*units
        totpoints += points
        GPA = totpoints / totunits
    print("GPA is ", ("%.2f" % GPA))
    print("total points = ", totpoints)
    print("total units = ", totunits)

One problem that I had with your answer is that we do not, in fact, know how many GPAs will be calculated, nor how many courses will be included in each. This is why I was wondering about a sentinel, but I (think I) figured out how to use the "internal sentinel" of the for loop that you mentioned.

I'm very interested in your matrix though, as I am just getting into studying arrays. Can you help me understand how using such a matrix would be advantageous over doing it the way I did? What would the for loop look like that would do the calculations?

Thanks again for your commitment to teaching!

p.s. any other notes you can offer about the program I wrote, in terms of better practices / ways to make it more efficient, would be greatly appreciated
Your Guide to Achieving IT Business Success

The IT Service Excellence Tool Kit has best practices to keep your clients happy and business booming. Inside, you’ll find everything you need to increase client satisfaction and retention, become more competitive, and increase your overall success.

You may have heard about DRY principle (Do not Repeat Yoursef). Actually, it also means that you should better split the functionality of your programs to the chunks that could be used separatly. The principle of more tight communication inside a chunk and less communication between the chunks with clearer interface is common also for the object oriented programming (here the chunks are the objects). I your case it means, that you should separate the data input procedure and the processing (calculation of GPA).

Some comments to your code:
GPA = totpoints / totunits

Open in new window

You are lucky here because you use Python 3. If the code was back-ported to Python 2, the results would be unexpected (integer instead of the float; the same problem would be observed in the C or C++ languages). The reason is that the division / needs at least one float to produce float.
grade = int(3)

Open in new window

This is unneccessarily complicated. You simply can assign grade = 3. But you probably already know that.

Whenever you need to map certain constants to some value (like 'A' to 4), you should use a mapping data structure. In Python it is a dictionary:
points = { 'A': 4, 'B': 3, 'C': 2, 'D': 1, 'F': 0 }

Open in new window

You can use float values for the points if you know the code should be backported to Python 2 (hence the division will produce float).

Whenever you write a serie of if's like in your case, and you cannot replace it by another mean (like a dictionary in your case), you should think about whether you can use the if/elif/elif/elif... combination. The difference is that the compiled results stops testing when the wanted branch was found. Anyway, dictionary is better in your case. It is more brief to write, easier to use, the resulting code is more readable.

Study the following solution and think about how to get the grade_matrix:

grade_matrix = [     # unknown number of units
    ['A', 'A', 'B', 'D'],                                     # student 1
    ['A', 'C', 'A', 'D', 'B', 'A'],                           # student 2
    ['B', 'D', 'B', 'D', 'A', 'A', 'B'],                      # student 3
    ['B', 'A', 'A', 'B', 'A'],                                # student 5
    ['A', 'F', 'B'],                                          # student 4
    ['C', 'A'],                                               # student 6
    ['A'],                                                    # student 7
    ['A', 'C', 'D', 'C'],                                     # student 8
    ['B', 'B', 'F', 'A', 'A', 'A', 'B', 'A', 'A', 'B'],       # student 9
    ['C', 'F', 'F', 'B', 'B', 'B', 'A', 'B', 'A', 'A', 'B'],  # student 10
    ['F', 'D', 'D', 'C', 'A', 'F', 'F'],                      # student 11
# A dictionary: unit grade --> points  
points = { 'A': 4, 'B': 3, 'C': 2, 'D': 1, 'F': 0 }

# Loop through all students.
for student_grades in grade_matrix:
    totunits = 0                          # init
    for grade in student_grades:
        totunits += points[grade]         # add points for the unit
    gpa = totunits / len(student_grades)

    print(gpa)                            # another student was processed

Open in new window

drewmoreAuthor Commented:
Awesome. (FYI, I turned this assignment in last week but this seems like a great learning exercise so I'd like to continue this dialogue. Does experts-exchange offer a method by which I can continue giving you points for continuing to respond to/teach me here? )

When I first looked at your solution, I was perplexed by the fact that neither student_grades nor grade was declared/initialized anywhere. They don't need to be, however, because of their implementation in the for loop: On line 21, you could write "for silly_geese in grade_matrix," and the program would know you were referring to the same lists. Likewise, on line 24, you could call each item in each list anything and Python would "bind" the items in those lists to that name, correct? This seems like a really simple realization, but it was very helpful one (for the record, I wrote this paragraph intending to ask 3 different questions, but in trying to verbalize them I answered each).

So, to implement the dictionary to "convert" one value to another that is assigned to it (in this case letter grades to a numeric points), the syntax is: nameOfDictionary[nameOfKey], and this "points to" the value associated with that key? (feel free to correct my terminology here)

To take this a step further: Your solution assumes that all courses have the same number of units. This is what enabled you to use len(student_grades) as the divisor in line 26 (correct?) What if we cannot make this assumption, and each grade is "weighted" differently?

I assume we would either want to:
a) create a second matrix, with a unit value for each grade in each student_grades list, and then get the program to multiply each number of points by its corresponding number of units, before dividing by the total number of units to produce a gpa.

b) Use a single matrix, but replace single letter grades with nested pairs containing both the letter grade and the number of units.

Which, if either, of these solutions would be best? How could it be implemented?

Thanks again..
> Does experts-exchange offer a method by which I can continue giving you points for continuing to respond to/teach me here?

No. At least I do not know if it does.

>  nameOfDictionary[nameOfKey], and this "points to" the value associated with that key?


> Your solution assumes that all courses have the same number of units. This is what enabled you to use len(student_grades) as the divisor in line 26 (correct?)

The stutent_grades is a list of marks. I do not know if they are mark from one course or marks assigned to more courses. In the later case, the structure is not good as you cannot say what courses were attended.

The len(student_grades) is simply the number of elements of the list.

> What if we cannot make this assumption, and each grade is "weighted" differently?

I do not understand. Do you mean that 'A' can be 8 sometimes? Then it is another task.
drewmoreAuthor Commented:
I'm going to post the following as a new question worth 500 points. Feel free to answer it there, and I will accept your solution so that you get those points. I understand this is not exactly how the system is designed to work, let me know if you're uncomfortable with it and I'll figure something else out

link to new question

Sorry, the language I used was confusing. I meant what if each course has a different number of units (hours). For instance, right now I'm taking two 5-hour courses and one 4-hour course. The point value for each letter grade is the same, but the grade I earn in the 4-hour course will affect my GPA slightly less than the grade I earn in the 5-hour courses. My GPA will be calculated as:

((points*5)+(points*5)+(points*4) ) / 14

So, along with the grade earned, the program would also have to collect the number of units of each course entered. I think we would either want to:

a) create a second matrix, with a unit value corresponding to each grade in each student_grades list. If so, how do we get Python to multiply items in one matrix by the corresponding items in another? (this seems like it'd be a powerful tool in general)

b) use a single matrix with nested pairs for each course containing the grade and unit value ( [('A', 4), ('B', 3)] , etc). If so, how do we get Python to multiply the first item in each pair by the second. Would it be as simple as:

for (x, y) in single_matrix:
points = x*y
totpoints += points              
totunits += y                          
GPA = totpoints / totunits

Which of these strategies do you think would work best. Is there another you think would work better?
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.