Getting list data from class object

Hello there, here is the link to the current lesson : https://www.codecademy.com/courses/learn-python-3/lessons/data-types/exercises/review?action=resume_content_item

I’m having troubles getting data from a list defined in the class.
here is the code :

class Student:
  testlist = [1,3,4]
  def __init__(self,name,year):
    self.name = name
    self.year = year
    self.grades = []
    
  def __repr__(self):
    pass
    
  def add_grade(self,grade):
    if type(grade) == Grade:
      print('Yes')
      self.grades.append(grade)
    else:
      print('No')
      

class Grade:
  minimum_passing = 65
  def __init__(self,score):
    if self.minimum_passing <= score:
      self.score = score
  
    
roger = Student('Roger van der Weyden',10)
sandro = Student('Sandro Botticelli',12)
pieter = Student('Pieter Bruegel the Elder',8)

pieter.add_grade(Grade(100))
print(pieter.grades[0])
print(pieter.name)
print(pieter.testlist)

and here are the answers :

Yes
<__main__.Grade object at 0x7fa396b729b0>
Pieter Bruegel the Elder
[1, 3, 4]

The previous lessons talked a little about the repr method that, in this, was used to return a certain data in the class object when it was simply called.
Something like this :

class naming:
  def __init__(self,name):
    self.name = name
  def __repr__(self):
    return self.name

and in case of something like

bob  = naming(bob)  
print(bob)

it return “bob” instead of the class object location

I searched a little on google and apparently repr can be used for alot of other things (as well as the str method), but i’m lacking knowledge to understed if that the thing i’m supposed to use (and if so, how) to get my list in the first example

Hi @illian00,

The problem is that the Python interpreter has not been informed regarding how you would like it to represent an instance of Grade. You can define a __repr__ method for Grade in order to specify that information. You can also consider defining a __str__ method.

See:

Note that these methods, if defined, must return a str.

An example of a __repr__ method for Grade could be:

  def __repr__(self):
    return str(self.score)

If you define that method as above, you’ll see a number when you print a Grade instance. Python will also invoke that method when you output a list of Grade instances, in order to represent each element within the square brackets. For example, the following would work:

pieter.add_grade(Grade(100))
pieter.add_grade(Grade(94))
pieter.add_grade(Grade(98))
print(pieter.grades)

Output:

[100, 94, 98]
3 Likes

Thanks for the explanation @appylpye !

One last thing that i quite don’t get, i’ve tried a few things to see how i can use the type “Grade” data, like add pieter.grades[0] + pieter.grades[1] and obviously it didn’t worked, so I tried to int()them beforehand, still throwing an error message (that it needed to be str to do so) so I tried to int(str()) and then i could finally use the numbers outside the class.
My question is, if the repr was defined to return a str(), why do i need to str() the data again afterward ?

If you try to return a number instead of a str from either the __repr__ or __str__ method in order to devise a means of adding up Grade instances, Python will raise a TypeError. Use a different strategy to do that.

The Python interpreter knows how to use the + operator to add numbers together, to concatenate strings, and perform certain other operations. However, if you want to be able to use the + operator to add up Grade instances, you need to inform the Python interpreter how to do that. Defining an __add__ method will help with that task.

See Emulating numeric types.

A simple example of an __add__ method for the Grade class would be:

  def __add__(self, other):
    return Grade(self.score + other.score)

Given that definition, you could do this:

grade_1 = Grade(100)
grade_2 = Grade(94)
grade_3 = Grade(98)
total_of_grades = grade_1 + grade_2 + grade_3
print(total_of_grades)

Output:

292

If you also would like to be able to use the built-in Python sum function on a list of Grade instances, you’ll need to do a little more work on the Grade class.

Note from the official documentation that the __repr__ method is meant for computing an “official” str representation of an object. In general, the __str__ method should be used for creating a user-friendly representation.

Edited on September 10, 2019 to clarify the mention of the sum function

2 Likes

Thanks for your answers and time :smile:!
I’m gonna play a little with those methods when I come back from work, since it seems that there is a lot of it.

When I currently think about it I don’t really get the use for those right now but I’m sure the lessons will expand on this matter soon enough

Thanks @appylpye !

1 Like

I’m afraid @appylpye solution will not work for you. I know that this is 2 years too late, but hopefully for others the solution I will provide will help. The given solution will still only return the object location of the list items back even with __repr__. You will want to create the following method in your Class:

def list_grades(self):
    listy = [str(grade.score) for grade in self.grades]
    return listy

and then you can use something like:

print(pieter.list_grades())

Hope this helps.

2 Likes

We could define the Grade class as follows in order to enable us to perform some mathematical operations directly on Grade objects, including the ability to pass a list of Grade objects to the sum() function:

class Grade:
  def __init__(self, score):
    self.score = score
  def __repr__(self):
    return str(self.score)
  def __add__(self, other): # add two Grade objects, result is a Grade
    return Grade(self.score + other.score)
  def __radd__(self, other): # sum a Grade + an int
    if other == 0:
      return self # needed for sum() to work
    else:
      return self.__add__(other)
  def __truediv__(self, other): # needed to divide a Grade by an int
    return self.score / other

Given that definition, we could also add the following methods to the Student class:

  def get_average(self):
    return sum(self.grades) / len(self.grades)
  def get_list_of_grades(self):
    # return a copy of the Student's Grades (as Grade objects)
    return list(self.grades)

Then we can do this:

pieter = Student('Pieter Bruegel the Elder',8)

pieter.add_grade(Grade(98)) 
pieter.add_grade(Grade(100)) 
pieter.add_grade(Grade(96)) 
pieter.add_grade(Grade(94)) 

print(pieter.get_average())
print(pieter.get_list_of_grades())

Output:

97.0
[98, 100, 96, 94]
3 Likes

any reason you couldn’t just print(pieter.grades[0].score)?

Yes, you can access the internal details of an instance of a class when necessary. However, an important aim of designing classes is to offer simplicity and convenience to the programmer. Toward that end, it is nice to provide documented methods that a programmer can call to perform tasks that are frequently needed.

3 Likes

This would just give you the object location instead of the grade value we are looking for. That’s why we defined a method in the above examples so that a list value is returned instead of an object location.

pieter.grades[0] is a grade object stored in a list inside the student object, pieter.grades[0].score returns the score value in the grade object. But I understand that it’s better to flesh out your class to return the value.