Learn Python: Classe - bonus challenge

I have tried to implement the bonus challenges, but I am really struggeling with the get.average:

At the bottom I added some Grades to the self.grades list in the Student class. I now tried to create a for loop which iterates over every value in the list and adds it to my total, which I then can divide by the length of my list in order to get the average. But I cant figure out what exactly to add to my total count, as I get a TypeError "unsupported operand for int and grade


class Student():
  
  def __init__(self, name, year):
    self.name = name
    self.year = year
    self.grades = []
    
  
  def add_grade(self, grade):
    if type(grade) == Grade:
      self.grades.append(grade)
    
  total_count = 0
  def get_average(self):
    for grade in self.grades:
      **self.total_count += ?**
    
    calculate_average = total_count / len(self.grades)
    print(calculate_average)
    
      
    
class Grade():
  
  minimum_passing = 65
  
  def __init__(self, score):
    self.score = score
  
  def is_passing(self):
    if self.score > self.minimum_passing:
      print("You made it!") 
    else:
      print("Sorry, score is not high enough")
    
    
    
roger = Student("Roger van der Weyden", 10)
sandro = Student("Sandro Botticelli", 12)
pieter = Student("Pieter Bruegel the Elder", 8)

**#In my understanding, I added some Grades to the list self.grades in Student**
pieter.add_grade(Grade(100))
pieter.add_grade(Grade(80))
pieter.add_grade(Grade(50))
pieter.add_grade(Grade(100))


Simplest would be to use the built in, sum(list) function.

This will only be zero with each new Student instance, but will not be zeroed out on subsequent calls to get_average(). The method needs to be self-contained (pun not intended).

The sum function works like so,

sum(list_object)

The method can be written as a single return statement…

return sum(self.grades) / len(self.grades)

Hi @cloud6474260085 and welcome to the Codecademy Forums.

Within the Forums, it is useful to provide a link to an exercise when it is to serve as the focus of a discussion.

Link to exercise: Learn Python 3: Classes: Review

Here, we need to access the numerical value that is stored within each Grade instance:

      **self.total_count += ?**

Based on the loop header, that would be grade.score.

However, as we design systems in which we define types, such as classes, we need to plan what information each instance of those types should store and how those instances are to behave.

In some respects, a Grade should behave as a number. We ought to be able to use the + operator to add two Grade instances together. It would be even nicer if, as @mtf suggests, we could use the sum function to total up a list of Grade instances.

See the following example, where we define a SummableThing class. Then try to use it as a model for adapting the Grade class, so that we can use the + operator and the sum function as desired:

class SummableThing:
  # Simple model for things that can be added up
  def __init__(self, val):
    self.val = val
  def __repr__(self):
    return str(self.val)
  def __add__(self, other):
    # Enable us to use the + operator on SummableThing objects
    return SummableThing(self.val + other.val)
  def __radd__(self, other):
    # Enable us to use the sum function on list of SummableThing objects
    if other == 0:
      return self
    else:
      return self.__add__(other)
n0 = SummableThing(99)
n1 = SummableThing(7)
n2 = SummableThing(8)
n3 = SummableThing(5)

print(sum([n0, n1, n2, n3]))

Output:

119

You can do a search for information on __add__ and __radd__ for additional insights.

don’t need radd for sum unless there are other types in there

… oh, sum uses 0 :: int as default identity, would have to specify a different identity value
… I’d kind of want sum to use the first value when present, or possibly even put the identity value on the right side … doesn’t quite add up, whatever, I guess what sum “wants” here is numbers.Number instances (but it still allows other things for some reason … legacy presumably but one would think it would have gotten tidied up in python 3)

oh and if 65 is minimum passing then 65 should pass :frowning:

Bit of an off-topic thought: What problems is the code supposed to solve?

Because if it’s computing an average, then one small function is enough for that.
The one other thing that seems like it could be useful is grouping up the attributes of a student.
So then, does this fulfill the requirements?

from collections import namedtuple


def average(xs):
    return sum(xs) / len(xs)


Student = namedtuple('Student', ['name', 'year', 'grades'])
roger = Student("Roger van der Weyden", 10, [])
sandro = Student("Sandro Botticelli", 12, [])
pieter = Student("Pieter Bruegel the Elder", 8, [100, 80, 50])
pieter.grades.append(100)
print(average(pieter.grades))

What does the rest of the code provide?
I think it’s healthy to have some hard limits to lean on.

1 Like

https://www.codecademy.com/courses/learn-python-3/lessons/data-types/exercises/review?action=resume_content_item

FYI, we know this will not work directly since we need to resolve each score from the Grade objects in self.grades.

return sum(self.grades) / len(self.grades)

A simple list comprehension will aid in the process…

return sum([x.score for x in self.grades]) / len(self.grades)
1 Like

The exercise is open ended in that step 9 invites the user to add some features to the program …

From here you could:

  • Write a Grade method .is_passing()

So a user could think about what math operations we’d often perform on grades, and implement those via operators, other methods, or external functions. These might include:

  • adding up a series of grades
  • averaging a series of grades
  • performing a weighted average of a series of grades
  • multiplying a grade by a number
  • converting a numerical score to a letter grade
  • converting a letter grade to a numerical score

We could think about how grades are like numbers as well as how they differ from numbers. For instance, it makes sense to provide a means of averaging grades or multiplying a grade by a number. But it doesn’t make sense to offer a means of multiplying a grade by a grade.

Yeah, it’s not a very good example of encapsulation, our having to deal with the quirks of the internal workings of the sum function.

Here’s a peek into what the sum function does with a list:

class SummableThing:
  # Simple model for things that can be added up
  def __init__(self, val):
    self.val = val
  def __repr__(self):
    return "SummableThing({})".format(self.val)
  def __add__(self, other):
    # Enable us to use the + operator on SummableThing
    print(" __add__:", self, other)
    return SummableThing(self.val + other.val)
  def __radd__(self, other):
    # Enable us to use the sum function on list of SummableThing
    print("__radd__:", self, other)
    if other == 0:
      return self
    else:
      return self.__add__(other)
st4 = SummableThing(4)
st5 = SummableThing(5)
st6 = SummableThing(6)
st7 = SummableThing(7)

print("result: {}".format(sum([st4, st5, st6, st7])))

Output:

__radd__: SummableThing(4) 0
 __add__: SummableThing(4) SummableThing(5)
 __add__: SummableThing(9) SummableThing(6)
 __add__: SummableThing(15) SummableThing(7)
result: SummableThing(22)

It initializes the result to 0. Then it iterates though the list, performing + operations, with each encountered item as the left operand, and the current accumulated result as the right operand.

2 Likes