Students become teachers - how is everybody doing? refactored

https://www.codecademy.com/en/courses/python-beginner-en-qzsCL/1/5?curriculum_id=4f89dab3d788890003000096

so i was just curious and i refactored the students become teacher track, this is what i ended up with:

class Person(object):
    def __init__(self,name,homework,quizzes,tests):
        self.name = name
        self.homework = homework
        self.quizzes = quizzes
        self.tests = tests
    def get_average(self):
        return average(self.homework) * .1 + average(self.quizzes) * .3 + average(self.tests) * .6
    def get_letter_grade(self):
        score = self.get_average()
        if score >= 90: return "A"
        elif score >= 80: return "B"
        elif score >= 70: return "C"
        elif score >= 60: return "D"
        else: return "F"
def get_class_average(students):
        return average([x.get_average() for x in students])
def average(numbers):
        return float(sum(numbers)) / len(numbers)

lloyd = Person("Lloyd",[90.0, 97.0, 75.0, 92.0],[88.0, 40.0, 94.0], [75.0, 90.0])
alice = Person("Alice",[100.0, 92.0, 98.0, 100.0],[82.0, 83.0, 91.0],[89.0, 97.0])
tyler = Person("Tyler",[0.0, 87.0, 75.0, 22.0],[0.0, 75.0, 78.0],[100.0, 100.0])
students = [lloyd, alice, tyler]
print get_class_average(students)
print tyler.get_average()
print tyler.get_letter_grade()
print get_letter_grade(get_class_average(students))

i am confused, this shouldn’t work, right? I am particular confused about this line:

print get_letter_grade(get_class_average(students))

get_letter_grade is a method part of the Person object, how on earth can it calculate the letter_grade for all students? This must be wrong, right?

4 Likes

Well, for me at least, when I put your refactored code into the lesson, I get

Oops, try again. One of the following is missing or broken when we tried 
to use it: alice, lloyd, tyler, students, get_class_average, get_letter_grade. 

And then in the module you get a

Traceback (most recent call last):
  File "python", line 28, in <module>
NameError: name 'get_letter_grade' is not defined

So, at least from the error, It can’t calculate it all. While get_letter_grade is defined in the code, it is defined with (self): does that make a difference? It is obviously necessary to calculating the averages and scores. It is almost as if the get_letter_grade has gotten “lost” and needs to be defined again? When you completely get rid of it you get the same error but on the module you get no errors but

83.8666666667
79.9
C
None

So it technically the code gives you everything you asked it but it does not have the class average through get_letter_grade just through print get_class_average(students). In some way, it is like get_letter_grade is irrelevant or just trying to repeat what was already printed.

4 Likes

hm… then it seems my browser was sending cached data, hold on. I have to re-test it. That was the error message i was expecting

1 Like

what about this:

class Person(object):
    def __init__(self,name,homework,quizzes,tests):
        self.name = name
        self.homework = homework
        self.quizzes = quizzes
        self.tests = tests
    def get_average(self):
        return average(self.homework) * .1 + average(self.quizzes) * .3 + average(self.tests) * .6
        
# functions
def average(numbers):
        return float(sum(numbers)) / len(numbers)
def get_class_average(students):
        return average([x.get_average() for x in students])
def get_letter_grade(score):
    if score >= 90: return "A"
    elif score >= 80: return "B"
    elif score >= 70: return "C"
    elif score >= 60: return "D"
    else: return "F"
lloyd = Person("Lloyd",[90.0, 97.0, 75.0, 92.0],[88.0, 40.0, 94.0], [75.0, 90.0])
alice = Person("Alice",[100.0, 92.0, 98.0, 100.0],[82.0, 83.0, 91.0],[89.0, 97.0])
tyler = Person("Tyler",[0.0, 87.0, 75.0, 22.0],[0.0, 75.0, 78.0],[100.0, 100.0])
students = [lloyd, alice, tyler]
print get_class_average(students)
print get_letter_grade(tyler.get_average())
print get_letter_grade(get_class_average(students))
4 Likes

Passes with the lesson fine. In the module you get

83.8666667
C
B
None
def get_letter_grade(score):
    if score >= 90: return "A"

Did you just condense the two into one? Or was self not needed?

3 Likes

looks right, right?

No, get_letter_grade is now a function, no longer a method, so self is no longer required

2 Likes

Ah. That makes sense. It did seem like that with it being under the method it was losing its function def but when you took it out and placed it outside of the Person method it told you to put it back because it was necessary. I also tried it without self but it messed with the class average so I thought unless I replaced self with something else to hold its place I was not going to be able to not use it. But you fixed both problems :smiley: Yeah it looks right. You got the correct output it asked for so yeah right seemingly.

2 Likes

yea i know, just a small change, i will change the order of the functions, then all should be clear an good, what do you think of the refactored code? better then the original?

1 Like

idk. In some ways I feel like your code sortof goes around the lesson’s purpose but I personally like the Person method rather than going through all this

def get_class_average(students):
    results = []
    for student in students:
        results.append(get_average(student))
    return average(results)

To me yours seems more straight forward and easier to follow and understand. Also, by this point has init been introduced in python? I can’t remember lol Also, however, [quote=“stetim94, post:4, topic:64511”]
def get_average(self):
return average(self.homework) * .1 + average(self.quizzes) * .3 + average(self.tests) * .6

functions

def average(numbers):
return float(sum(numbers)) / len(numbers)
def get_class_average(students):
return average([x.get_average() for x in students])
[/quote]

This part seems to be more difficult to produce then the original but is shorter and more efficient. However, with the average learner, this is might not be duplicated without a some trouble or with less understanding than the original. So all in all I think it comes down to experience and preference honestly.

3 Likes

of course this requires more understanding, but it does cut back on quit a few lines of code.

3 Likes

Yeah I can’t argue with that. Like I said I think yours is shorter and more efficient. Just being able to help the learners be able to code a code like that by the time the lesson comes though is a different story. Now, they might could as is but at least the lessons before it would have to go in a different route in lead up to that your refactored code.

3 Likes

Hi @stetim94 and @bibleman13 ,

This thread attracted my attention because I also find it interesting to refactor some of the material in the Python track. The following is a modification of the code that @stetim94 posted in this thread. It includes some extra wrapper functions so that it passes the SCT for the final exercise.

# Student Becomes the Teacher
# 9. How is Everybody Doing?
# https://www.codecademy.com/en/courses/python-beginner-en-qzsCL/1/5
class Person(object):
    def __init__(self,name,homework,quizzes,tests):
        self.name = name
        self.homework = homework
        self.quizzes = quizzes
        self.tests = tests
    def get_average(self):
        return Grade(average(self.homework) * .1 + average(self.quizzes) * .3 + average(self.tests) * .6)
    def get_name(self):
        return self.name
    
class Course(object):
    def __init__(self, students, name):
        self.students = list(students)
        self.name = name
    def get_class_average(self):
        return Grade(average([x.get_average().get_number_grade() for x in self.students]))
    def get_class_name(self):
        return self.name

class Grade(object):
    def __init__(self, score):
        self.score = score
    def get_number_grade(self):
        return self.score
    def get_letter_grade(self):
        if self.score >= 90: return "A"
        elif self.score >= 80: return "B"
        elif self.score >= 70: return "C"
        elif self.score >= 60: return "D"
        else: return "F"

# global function
def average(numbers):
    return float(sum(numbers)) / len(numbers)


lloyd = Person("Lloyd",[90.0, 97.0, 75.0, 92.0],[88.0, 40.0, 94.0], [75.0, 90.0])
alice = Person("Alice",[100.0, 92.0, 98.0, 100.0],[82.0, 83.0, 91.0],[89.0, 97.0])
tyler = Person("Tyler",[0.0, 87.0, 75.0, 22.0],[0.0, 75.0, 78.0],[100.0, 100.0])
students = [lloyd, alice, tyler]
our_class = Course(students, "Intro to Python")
class_average = our_class.get_class_average()
print(our_class.get_class_name())
print(class_average.get_number_grade())
print(class_average.get_letter_grade())
tyler_grade = tyler.get_average()
print(tyler.get_name())
print(tyler_grade.get_number_grade())
print(tyler_grade.get_letter_grade())

###### Wrapper functions to satisfy the SCT ######
def get_class_average(students):
    return Course(students, "CS").get_class_average().get_number_grade()
def get_letter_grade(score):
    return Grade(score).get_letter_grade()
    
print(get_class_average(students))
print(get_letter_grade(get_class_average(students)))
4 Likes

yea, the problem of my refactoring is that if you want the letter grade for class average you get this:

print get_letter_grade(get_class_average(students))

your solution is also really nice @appylpye

2 Likes