Class Attribute Representation (or why does it print this <main.Grade object at 0x7f2b2baf4be0>?)

Hey hello did anyone solve the review part correctly?
This is my code:

"""
A class is a template for a data type. It describes the kinds of information that class will hold and how a programmer
will interact with that data. Define a class using the class keyword. PEP 8 Style Guide for Python Code
recommends capitalizing the names of classes to make them easier to identify.
"""
"""Instantiation
A class doesn't accomplish anything simply by being defined. A class must be instantiated. In other words, we must 
create an instance of the class, in order to breathe life into the schematic.
Instantiating a class looks a lot like calling a function. We would be able to create an instance of our defined CoolClass as follows:

"""
# example : cool_instance = CoolClass()

"""Above, we created an object by adding parentheses to the name of the class. We then assigned that
 new instance to the variable cool_instance for safe-keeping.
"""
"""Instantiation takes a class and turns it into an object, the type() function does the opposite of that. When called 
with an object, it returns the class that the object is an instance of."""
"""Methods are functions that are defined as part of a class. The first argument in a method is always the object that is 
calling the method. Convention recommends that we name this first argument self. Methods always have at least this one argument.
We define methods similarly to functions, except that they are indented to be part of the class.
"""
# Little project about classes an OOB
import datetime
time_now= datetime.datetime.now()
class Student:


    def __init__(self, name, year,today):
        self.name = name
        self.year = year
        self.grades = []
        self.attendance = {}




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



    def get_average(self):
        return sum(self.grades) / len(self.grades)


class Grade:

    minimum_passing = 65
    def __init__(self, score):
        self.score = score

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



roger = Student("Roger van der Weyden", 10,"11/22/2018")
sandro = Student("Sandro Botticelli", 12,"11/22/2018")
peter = Student("Pieter Bruegel the Elder", 8,"11/22/2018")
print(roger)
print(sandro)
print(peter)

peter.add_grade(Grade(100))
print(roger.attendance)
print(peter.grades)
print(peter.get_average())
print(peter.attendance)

def is_passing(self):

    if self.score >self.minimum_passing:
        return "Is passing"
    else:
        return "Is failing"
def _update_attendance(self):

    if self.today == time_now.strftime("%x"):

        self.attendance.update({self.today:True})
    else:
        self.attendance.update({self.today:False})

the output i get:
<main.Student object at 0x0000000002945208>
<main.Student object at 0x000000000290B9E8>
<main.Student object at 0x000000000290BA58>
{}
[100]
Traceback (most recent call last):
line 69, in
print(peter.get_average())
, line 45, in get_average
return sum(self.grades) / len(self.grades)
TypeError: unsupported operand type(s) for +: ‘int’ and ‘Grade’

1 Like

The code attached below includes a few comments and print statements to check the code. The attendance dictionary could be updated from a spreadsheet, if needed. It is not type-checked here.

class Student:
  
  def __init__(self, name, year):
    self.name = name
    self.year = year
    self.grades = [] # type: List
    self.attendance = {}  # type: Dict
  
  def __repr__(self):
    return "{0} is in year {1}. This student's grades are {2}.".format(self.name, self.year, self.grades)
      
  def add_grade(self, grade):
    self.grade = grade
    if type(self.grade) == Grade: 
      self.grades.append(self.grade.score) # attribute from Grade
  
  def get_average(self):
    return "{0}'s average is {1}.".format(self.name, sum(self.grades) / len(self.grades))
  
  
class Grade:
  minimum_passing = 65
  
  def __init__(self, score):
    self.score = score
    
  def is_passing(self):
    return self.score >= self.minimum_passing
    
roger = Student("Roger van der Weyden", 10)
sandro = Student("Sandro Botticelli", 12)
pieter = Student("Pieter Bruegel the Elder", 8)

for i in range(95, 101):
    pieter.add_grade(Grade(i))

pieter.attendance["2019-01-08"] = True
pieter.attendance["2019-01-07"] = False

print(roger)
print(sandro)
print(pieter)
thumbs_up = Grade(99)
thumbs_down = Grade(64)

print(pieter.get_average()) # use get_average() vice get_average
# print(pieter.get_average) # uncomment this to see what prints
print(thumbs_up.is_passing()) # True for this example
print(thumbs_down.is_passing())

print(pieter.attendance)

giulia22,

grade is of type Grade. While grade is an integer, Grade is not. You want to pass the score from Grade like this:

def add_grade(self, grade):
    self.grade = grade
    if type(self.grade) == Grade: 
      self.grades.append(self.grade.score) # attribute from Grade
2 Likes

I notice the above examples are using type(), but the SCT may be expecting isinstance()

def add_grade(self, grade):
    if isinstance(grade, Grade):
      self.grades.append(grade)

For the average, I created a helper method that returns a list of numbers…

  def get_grades(self):
    return [x.score for x in self.grades]

Now the method can be called upon to simplify get_average()

  def get_average(self):
    grades = self.get_grades()
    return sum(grades) / len(grades)

The is_passing method can be handed the average…

print(Grade(pieter.get_average()).is_passing())
1 Like
class Student:
  pass
  def __init__(self, name, year):
    self.name = name
    self.year = year
    self.grades = []
  def add_grade(self, grade):
    self.grade = grade
    if type(self.grade) == type(Grade):
      (self.grades).append(self.grade)
    else:
      False


roger = Student("Roger van der Weyden", 10)
sandro = Student("Sandro Botticelli", 12)
pieter = Student("Pieter Bruegel the Elder", 8)

class Grade:
  minimum_passing = 65
  def __init__(self, score):
    self.score = score

new = Grade(100)
pieter.add_grade(new)

I am having trouble moving past step 8:
Create a new Grade with a score of 100 and add it to pieter 's .grades attribute using .add_grade() .

I have tried many different variations. Some have come back asking if I added grade to pieter’s grades using add_grade? another says my function has no len()…

I can’t seem to break this code. any help?

def add_grade(self, grade):
    self.grade = grade
    if type(self.grade):
      (self.grades).append(self.grade)
    else:
      False

I took away the type(self.grade) == type(Grade):

and worked…not sure why other than there was no obstruction to perform the add_grade function…

That line may be removed, since the body contains code it serves no purpose.

That’s creating a new attribute that also serves no real purpose. Work with the parameter and don’t assign it to the object.

Let’s play around with this…

>>> class Grade:
	def __init__(self, grade):
		self.grade = grade

		
>>> a = Grade(100)
>>> type(a)
<class '__main__.Grade'>
>>> type(a) == 'Grade'
False
>>> type(a) == Grade
True
>>> 

So we see that type(object) can be equated to the class.

if type(grade) == Grade:
    self.grades.append(grade)

The else clause in this instance could be either, return False or not have a default action.

Another possibility might be to raise a TypeError exception with a message to the user. No return will be required.

Can anyone explain what’s going on with this part?

if type(grade) is Grade

>>> class Grade:
        def __init__(self, grade):
            self.grade = grade


>>> grade = Grade(80)
>>> grade
<__main__.Grade object at 0x7f97481dbe48>
>>> isinstance(grade, Grade)
True
>>> type(grade) is Grade
True
3 Likes

Thanks. I’m pretty sure I have an understanding of this now.

1 Like

This solved the issue for me. Thanks!

Here’s my code. After executing the code below, I get <__main__.Grade object at 0x7f39cfcb5c88> message.

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)
  
  def get_average(self):
    total = 0
    for grade in self.grades:
      total += grade.score
    avg = total / len(self.grades)
    return avg
    
roger = Student("Roger van der Weyden", 10)
sandro = Student("Sandro Botticelli", 12)
pieter = Student("Pieter Bruegel the Elder", 8)

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
    
    
grade1 = Grade(100)
pieter.add_grade(grade1)
print(pieter.name)
print(pieter.year)
print(grade1.is_passing())
print(Grade(pieter.get_average()))

Hi @cloud4317698779,

If you would like to customize the string representation of instances of Grade so that the output is more informative, you can add a __str__ method to the definition of that class, as follows:

  def __str__(self):
    return str(self.score)
1 Like

This is my code, when i print pieter.get_average, i get:
<bound method Student.get_average of <main.Student object at 0x7f46b43a7c18>>

how can i use repr to get the actual number?

thanks!

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 get_average(self):
return sum(self.grades) / len(self.grades)

roger = Student(‘Roger van der Weyden’, 10)
sandro = Student(‘Sandro Botticelli’, 12)
pieter = Student(‘Pieter Bruegel the Elder’, 8)

class Grade:
minimum_passing = 65
def init(self, score):
self.score = score
def is_passing(self):
if self.score >= self.minimum_passing:
print(‘pass’)
else:
print(‘sorry’)
def repr(self):
return str(self.score)

pieter.add_grade(Grade(100))
pieter.add_grade(Grade(98))
pieter.add_grade(Grade(75))
pieter.add_grade(Grade(66))

print(pieter.get_average)

Hi @thedunkpub86,

There are several issues to address regarding your code, and other users may have pertinent insights that they would like to share with you. For that to happen, those users will need to read, copy, paste, test, and modify your code. However, your posted code is not formatted, making it difficult to work with it.

For advice on formatting code for posting, please see How to ask good questions (and get good answers). You may then edit your post in order to format the code.

Edited on August 7, 2019 to add the following:

Note that in this final line of code, you have not called the get_average method of the Student class, since parentheses were not provided following the name of the method:

print(pieter.get_average)

That is why this reference to the method was displayed:

<bound method Student.get_average of < **main** .Student object at 0x7f46b43a7c18>>
2 Likes

Thank you very much!

I’m new to the site. I’ll try ask questions properly next time.

1 Like

Thanks, @thedunkpub86.

Note that there is still some debugging to be done. This statement would call the method:

print(pieter.get_average())

However, with all of your preceding code as is, you would see an error message similar to the following:

Traceback (most recent call last):
  File "/Python/glenn/StudentGrade.py", line 124, in <module>
    print(pieter.get_average())
  File "/Python/glenn/StudentGrade.py", line 100, in get_average
    return sum(self.grades) / len(self.grades)
TypeError: unsupported operand type(s) for +: 'int' and 'Grade'

Do you know why this error occurs? The final two lines of the error message form a good clue that can guide us to a resolution.

I got it to work with the help of a member’s answer here in the topic, but i don’t really understand why it works.

 def get_average(self):
    Total = 0
    for grade in self.grades:
      Total += grade.score
      avg = Total / len(self.grades)
      return avg

Hi @thedunkpub86,

There is a problem with the indentation in your get_average method. The final two statements need to execute after all the grades have been added up. However, you have those statements inside the for loop, so they get executed during the first iteration of the loop. In fact, due to the return, it becomes the only iteration.

It is conventional to begin the name of a variable with a lowercase letter, so let’s change Total to total.

Making all those corrections, we then have:

  def get_average(self):
    total = 0
    for grade in self.grades:
      total += grade.score
    avg = total / len(self.grades)
    return avg

Previously, you had this in your get_average method:

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

A problem with that line was that the get_average list contains Grade objects. A Grade object contains a number as an instance variable, but that does not make a Grade object be a number. Therefore, Python does not know how to apply the sum function to a list of them. With some work, you can make that happen, however, the above revision of your get_average method avoids that issue by adding up the score instance variables within the loop.

There is still a remaining problem, but it can easily be corrected. Your original post had this:

Note that the above quote has been revised to show the underscores.

As things stand now, this statement does not use the __repr__ method of Grade to display the actual number, though it may appear that it does:

print(pieter.get_average())

Do you know why? Consider what type of result is returned by the get_average method. Is it a Grade or is it something else? Once we have this figured out, a minor revision can rectify the issue.

This post was edited on August 10, 2019 to refine some of the code.

Thank you!
I still don’t get it but that’s not your fault!

thanks again!