FAQ: Learn Python: Classes - Review

try/except handles the AssertionError

But isn’t assert more for hardening code in the test phases? Even try is a little over the top in this instance, wouldn’t you say?

so just use conditionals?

At the outset, one would say, yes. We will need to play out the exceptions at some point in the development of our program, but the simplest route first is as straight forward as we can get.

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

I’ve mentioned before that it is folly to break the object down at this point. We’ve just confirmed that the grade value is a Grade object. That means our methods can be tuned in to anything those objects have to throw at us. DON’T store only the score.

this is what got me, lol. Thanks

1 Like

Just wanted to share my code after building out the bonus sections. Any tips on how to improve it would be greatly appreciated.

from datetime import datetime
import random

class Student:
  def __init__(self, name, year):
    self.name = name
    self.year = year
    self.grades = {}
    self.attendance = {}

  def __repr__(self):
    return 'Student:  ' + self.name
    
  def add_grade(self, grade):
    if type(grade) is Grade:
      self.grades[grade.subject] = [grade.score, grade.is_passing]

  def get_average(self):
    total_score = 0
    if len(self.grades) > 0:
      for subject in self.grades:
        total_score += self.grades[subject][0]
      return total_score / len(self.grades)
    else:
      return 'No grades added yet'

  def set_attendance(self, a_date):
    now = datetime.strptime(a_date, '%m/%d/%Y')
    year = now.year
    month = now.month
    day = now.day
    self.attendance[f'{month}/{day}/{year}'] = False

  def print_report_card(self):
    gpa = 'GPA'
    stdnt = 'Student'
    yr = 'Year'
    absc = 'Absences'
    print('REPORT CARD')
    print(f'{stdnt:<{15}}:' + f' {self.name:<{25}}')
    print(f'{yr:<{15}}:' + f' {self.year:>0{2}}')
    print(f'{absc:<{15}}:' + f' {len(self.attendance):>0{2}}')
    print('-' * 41)
    print('GRADES')
    for subject in self.grades:
      print(f'{subject:<{15}}:' + f' {self.grades[subject][0]:>{6}.{2}f}' + f' {self.grades[subject][1]}')
    print('-' * 41)
    print(f'{gpa:<{15}}:' + f' {self.get_average():>{6}.{2}f}')
    print('\n')
      
class Grade:
  def __init__(self, subject, score):
    minimum_passing = 65
    self.subject = subject
    self.score = score
    if self.score >= minimum_passing:
      self.is_passing = 'PASS'
    else:
      self.is_passing = 'FAIL'

# Create 3 students
roger = Student('Roger van der Weyden', 10)
sandro = Student('Sandro Botticelli', 12)
pieter = Student('Pieter Bruegel the Elder', 8)
students = [roger, sandro, pieter]

# Add Roger's grades
roger.add_grade(Grade('Math', float(random.randint(60,100))))
roger.add_grade(Grade('Social Studies', float(random.randint(60,100))))
roger.add_grade(Grade('Language Arts', float(random.randint(60,100))))
roger.add_grade(Grade('Chemistry', float(random.randint(60,100))))

# Add Sandro's grades
sandro.add_grade(Grade('Math', float(random.randint(60,100))))
sandro.add_grade(Grade('Social Studies', float(random.randint(60,100))))
sandro.add_grade(Grade('Language Arts', float(random.randint(60,100))))
sandro.add_grade(Grade('Chemistry', float(random.randint(60,100))))

# Add Pieter's grades
pieter.add_grade(Grade('Math', float(random.randint(60,100))))
pieter.add_grade(Grade('Social Studies', float(random.randint(60,100))))
pieter.add_grade(Grade('Language Arts', float(random.randint(60,100))))
pieter.add_grade(Grade('Chemistry', float(random.randint(60,100))))

# Add Roger's Absences
roger.set_attendance('1/1/2021')

# Add Sandro's Absences
sandro.set_attendance('2/1/2021')
sandro.set_attendance('2/2/2021')

# Add Pieter's Absences
pieter.set_attendance('3/1/2021')
pieter.set_attendance('3/2/2021')
pieter.set_attendance('3/3/2021')

# Print out the report cards
for student in students:
  student.print_report_card()

Update: removed a lot of the duplicate code with some for loops.

from datetime import datetime
import random

class Student:
  def __init__(self, name, year):
    self.name = name
    self.year = year
    self.grades = {}
    self.attendance = {}

  def __repr__(self):
    return 'Student:  ' + self.name
    
  def add_grade(self, grade):
    if type(grade) is Grade:
      self.grades[grade.subject] = [grade.score, grade.is_passing]

  def get_average(self):
    total_score = 0
    if len(self.grades) > 0:
      for subject in self.grades:
        total_score += self.grades[subject][0]
      return total_score / len(self.grades)
    else:
      return 'No grades added yet'

  def set_attendance(self, a_date):
    now = datetime.strptime(a_date, '%m/%d/%Y')
    year = now.year
    month = now.month
    day = now.day
    self.attendance[f'{month}/{day}/{year}'] = False

  def print_report_card(self):
    gpa = 'GPA'
    stdnt = 'Student'
    yr = 'Year'
    absc = 'Absences'
    print('REPORT CARD')
    print(f'{stdnt:<{15}}:' + f' {self.name:<{25}}')
    print(f'{yr:<{15}}:' + f' {self.year:>0{2}}')
    print(f'{absc:<{15}}:' + f' {len(self.attendance):>0{2}}')
    print('-' * 41)
    print('GRADES')
    for subject in self.grades:
      print(f'{subject:<{15}}:' + f' {self.grades[subject][0]:>{6}.{2}f}' + f' {self.grades[subject][1]}')
    print('-' * 41)
    print(f'{gpa:<{15}}:' + f' {self.get_average():>{6}.{2}f}')
    print('\n')
      
class Grade:
  def __init__(self, subject, score):
    minimum_passing = 65
    self.subject = subject
    self.score = score
    if self.score >= minimum_passing:
      self.is_passing = 'PASS'
    else:
      self.is_passing = 'FAIL'

# Create 3 students
roger = Student('Roger van der Weyden', 10)
sandro = Student('Sandro Botticelli', 12)
pieter = Student('Pieter Bruegel the Elder', 8)
students = [roger, sandro, pieter]

# Add grades for all students
subjects = ['Math', 'Social Studies', 'Language Arts', 'Chemistry']
for student in students:
  for subject in subjects:
    random_grade = random.randint(60,100)
    student.add_grade(Grade(subject, float(random_grade)))

# Add random absences for all students
for student in students:
  for i in range(random.randint(1,10)):
    student.set_attendance(f'{random.randint(1,12)}/{random.randint(1,28)}/2021')

# Print out the report cards
for student in students:
  student.print_report_card()
1 Like

Can anybody tell me what does this statement mean inside add_grade method:

  def add_grade(self, grade):
    if type(grade) is Grade:

In this code

class Student:
  def __init__(self, name, year):
    self.name = name
    self.year = year
    self.grades = []
  def add_grade(self, grade):
    if type(grade) is Grade:
      self.grades.append(grade)

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

class Grade:
  minimum_passing = 65

We are checking if the argument grade is an instance of the Grade class. Because we only want to add grades that are actually grades (meaning they are objects of the Grade class), we have to use this conditional.

but type(grade) gives class int. What logic does it make comparing class int to another class. seems complex to me

That’s because you’ve incorrectly passed in an integer for the grade parameter. You should be passing in an instance of Grade.

but instance not yet created so how can I pass it to grade?

There is no instance of grade object in Grade method. So how it is comparing type(grade) is Grade in Student method.

At line number 11 why you have empty list lying there???

you can create an instance of Grade class? Then use this instance/object when calling the add_grade method

1 Like

I am not even getting this add_grade method. Never seen such statement in previous classes

pieter.add_grade(Grade(100))

How is this even working

Why not? Methods and calling methods has been covered, right?

or is it confusing that we pass another class instance/object as argument to method call?

Hi everyone! I was wondering if someone could explain this to me:

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

pieter.add_grade(Grade(100))

In this code fragment from my Review exercise, I am adding Pieter’s grade to his .grade() attribute. However, I am confused as to how this works.

I did not instantiate the Grade class before this. How am I still able to pass it into the .add_grade() method? Can methods / functions take classes as arguments? Or is there another explanation.

Appreciate if anyone could shed some light on this!

you did, here:

Grade(100)

of course you could break this down into two steps:

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

but one step is also sufficient here

1 Like

Deleted because I realised on my own how

if type(grade) is Grade:

actually works

The clue for me was in

pieter.add_grade(Grade(100))
pieter.add_grade(100) this does not work