'Print data from Class instance

I want to print out data from a Class instance. I tried including data in str function, but this only works when value is at its default (an empty list). After I've added data to the list, I can only get memory objects printed. Can anyone help me troubleshoot why this is happening? I want to be able to print out objects to use in debugging.

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)
        else:
            pass
    
    def __str__(self):
        return str(self.grades)

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

print(pieter)
[]  # Returns empty list of grades as it was initiated. 

class Grade():
    minimum_passing=65
    
    def __init__(self,score):
        self.score=score
    
    def is_passing(self):
        if self.score >= self.minimum_passing:
            return True
        else:
            return False

pieter.add_grade(Grade(100))
pieter.add_grade(Grade(40))

print(pieter)
[<__main__.Grade object at 0x000002B354BF16A0>, <__main__.Grade object at 0x000002B354BF13A0>]


Solution 1:[1]

When you tell it to print 'pieter', you are telling it to attempt to print the object. What you need to do is add a print function in the class or use the specific variable in the object. Try adding something like this:

def print_object(self):
    print("The name is: {self.name}")
    print("The year is: {self.year}")
    print("The grades are: {self.grades}")

If you have any questions, please feel free to reach out.

Solution 2:[2]

The list.__str__() method uses repr() to get the representation of the list elements. So this is showing the repr() of all the Grade objects in the grades list. Since Grade doesn't have a custom __repr__() method, you get the default representation.

You could define Grade.__repr__() to specify how you want grades to appear, or you could change the Student.__str__() method to get the scores from the Grade objects in the list.

class Student():
    
    def __init__(self,name,year):
        self.name=name
        self.year=year
        self.grades=[]
    
    def add_grade(self, grade):
        if isinstance(grade, Grade):
            self.grades.append(grade)
        else:
            pass
    
    def __str__(self):
        return str([str(g) for g in self.grades])

Solution 3:[3]

The problem is that Grade itself doesn't have a neat representation as a string. Thus the str(self.grades) call can only print a list of abstract instance data. You could include a representation:

class Grade():

    # the other stuff goes here...

    def __repr__(self):
        return f'Grade({self.score}'

Now print(pieter) prints:

[Grade(100), Grade(40)]

However, I wouldn't use the __str__() magic method to print the grades. It's clearer to define an explicit method such as print_grades() for this purpose. __str__() is normally used to output all the relevant information about a class (for debug purposes etc.) So in your case, it should return all the student info too, not just the grades.

Solution 4:[4]

In your add_grade method, you are inserting a Grade object into self.grades, and then when you print self.grades you get a list of Grade objects. Maybe you want to insert the score field from the Grade object into self.grades. That way you can print out the scores when you print out a student. Like so:

def add_grade(self, grade):
    if type(grade) == Grade:
        self.grades.append(grade.score)

Also, there is no need to write the else statement in the method, it will automatically return None if the argument passed to it is not a Grade object.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 CowboyUp-Hacker
Solution 2 Barmar
Solution 3
Solution 4 Jake Korman