# Program to compute final grade for weighted assignments

I wrote a "gradebook" program. This takes in grades for various assignments. The assignments types are weighted differently and final grade is computed at the end. The code works but I can't help but think I am being ham handed in my approach. Should I be using a dictionary instead of storing things like hw_final, lab_final, and exam_final as global variables?

hw_tally = 0
hw_weight_count = 0
hw_final = 0
lab_tally = 0
lab_weight_count = 0
lab_final = 0
exam_tally = 0
exam_weight_count = 0
exam_final = 0

global hw_tally, hw_weight_count, hw_final, lab_tally, lab_weight_count, lab_final, exam_tally, exam_weight_count, exam_final
if kind == 'hw':
hw_weight_count += weight
hw_tally += hw_temporary
hw_final = hw_tally/hw_weight_count
elif kind == 'lab':
lab_weight_count += weight
lab_tally += lab_temporary
lab_final = lab_tally/lab_weight_count
elif kind == 'exam':
exam_weight_count += weight
exam_tally += exam_temporary
exam_final = exam_tally/exam_weight_count

assignment('hw', 81)
assignment('exam', 90, 4)
assignment('hw', 100, 2)
assignment('lab', 84, 3)
assignment('lab', 90, 2)
assignment('exam', 78)
assignment('exam', 85)
assignment('lab', 88)
assignment('exam', 75)

hw_weight_percent = 0.4
lab_weight_percent = 0.1
exam_weight_percent = 0.5

final_grade = ((hw_final * hw_weight_percent) + (lab_final *
lab_weight_percent) + (exam_final * exam_weight_percent))

• @depperm Comments are for seeking clarification to the question, and may be deleted. Please put all suggestions for in answers, even if they are brief. May 1, 2018 at 16:25

Yes it would be better to use a dictionary, that would reduce the amount of global variables. Also you do not need to store and calculated the ..._final all the times, you can calculate it only when you need need it. The (nested) dictionary to store the grades could be:

scores = {
"hw"   : { "tally" : 0, "weight_count" : 0} ,
"lab"  : { "tally" : 0, "weight_count" : 0} ,
"exam" : { "tally" : 0, "weight_count" : 0} ,
}


Then the assignment function can be made shorter, without calculating the final at this moment:

def assignment(kind, grade, weight=1):
global scores
scores[kind]["weight_count"] += weight


We could write another function to calculate the final of a kind:

def final_of_kind(kind):
global scores
return scores[kind]["tally"] / scores[kind]["weight_count"]


And the rest of the program is almost the same:

assignment('hw', 81)
assignment('exam', 90, 4)
assignment('hw', 100, 2)
assignment('lab', 84, 3)
assignment('lab', 90, 2)
assignment('exam', 78)
assignment('exam', 85)
assignment('lab', 88)
assignment('exam', 75)

hw_weight_percent = 0.4
lab_weight_percent = 0.1
exam_weight_percent = 0.5

final_grade = (  (final_of_kind("hw")   * hw_weight_percent)
+ (final_of_kind("lab")  * lab_weight_percent)
+ (final_of_kind("exam") * exam_weight_percent))



Note: the alignment in the assignment of final_grade is frowned up on sometimes, because it is not according to PEP8, but I use it for readability.

• thanks Jan. I like your solution more it is easier for me to follow. The above solution is beyond my level of experience May 2, 2018 at 15:31
1. Split assignment into three functions. homework, exam and labwork. Take:

def homework(grade, weight=1):
global hw_tally, hw_weight_count, hw_final
hw_weight_count += weight
hw_tally += hw_temporary
hw_final = hw_tally/hw_weight_count

2. Change homework into a class.

class Scores:
def __init__(self):
self.tally = 0
self.weight_count = 0

@property
def final(self):
return self.tally / self.weight_count

self.weight_count += weight


Now you can use the above class to total all the separate things. And then perform your total on Scores.final.

3. Group the exam types into another class, say StudentScores.

4. Expand Scores to also take the percentage. And make final return the percentage.
5. Move your final calculation into StudentScores.

This can make:

class Scores:
def __init__(self, percentage=1):
self.tally = 0
self.weight_count = 0
self.percentage = percentage

@property
def final(self):
return self.tally / self.weight_count * self.percentage

self.weight_count += weight

class StudentScores:
def __init__(self, scores):
total = sum(scores.values())
self._scores = {
key: Scores(score / total)
for key, score in scores.items()
}

def __getattr__(self, name):
return self._scores[name]

@property
return sum(scores.final for scores in self._scores.values())

student = StudentScores({
'homework': 0.4,
'labwork': 0.1,
'exam': 0.5
})



This uses a lot of classes, and isn't really the best way if you're taking data from a file. And so if you store your results as a JSON file, and read it into Python however you want. Then you can default the weights into the JSON data. And then perform the rest of the code in a couple of comprehensions, wrapped in a sum:

results = {
'homework': [
0.4,
[
[81],
[100, 2]
]
],
'labwork': [
0.1,
[
[84, 3],
[90, 2],
[88]
]
],
'exam': [
0.5,
[
[90, 4],
[78],
[85],
[75]
]
]
}

def default_weights(results):
return [
[percentage, [(score + [1])[:2] for score in scores]]
for percentage, scores in results.values()
]