import datetime
class Person(object):
def __init__(self, name):
"""create a person called name"""
self.name= name
self.birthday= None
self.lastName= name.split(' ')[-1]
def getLastName(self):
"""return self's last name"""
return self.lastName
def __str__(self):
"""return self's name"""
return self.name
def setBirthday(self,month,day,year):
"""sets self's birthday to birthDate"""
self.birthday= datetime.date(year,month,day)
def getAge(self):
"""returns self's current age in days"""
if self.birthday == None:
raise ValueError
return (datetime.date.today() -self.birthday).days
def __lt__(self, other):
"""return True if self's name is lexicographically
less than other's name, and False otherwise"""
if self.lastName == other.lastName:
return str(self.name) < str(other.name)
return str(self.lastName) < str(other.lastName)
p1 = Person('Mark Zuckerberg')
p1.setBirthday(5,14,84)
p2 = Person('Drew Houston')
p2.setBirthday(3,4,83)
p3 = Person('Bill Gates')
p3.setBirthday(10,28,55)
p4 = Person('Andrew Gates')
p5 = Person('Steve Wozniak')
personList= [p1, p2, p3, p4, p5]
for e in personList:
print(e)
personList.sort()
for e in personList:
print(e)
class MITPerson(Person): #sub class of person
nextIdNum= 0 # next ID number to assign
def __init__(self, name):
Person.__init__(self, name) # initialize Person attributes
self.idNum= MITPerson.nextIdNum# MITPersonattribute: unique ID
MITPerson.nextIdNum+= 1
def getIdNum(self):
return self.idNum
# sorting MIT people uses their ID number, not name!
def __lt__(self, other):
return self.idNum< other.idNum
def speak(self, utterance):
return(self.getLastName() + " says: " + utterance)
m3 = MITPerson('Mark Zuckerberg')
Person.setBirthday(m3,5,14,84)
m2= MITPerson('Drew Houston')
Person.setBirthday(m2,3,4,83)
m1 = MITPerson('Bill Gates')
Person.setBirthday(m1,10,28,55)
MITPersonList= [m1, m2, m3]
for e in MITPersonList:
print(e) # prints in the order in the list
MITPersonList.sort()
for e in MITPersonList:
print(e) #prints in the order is id as designed in __it__
p1 = MITPerson('Eric')
p2 = MITPerson('John')
p3 = MITPerson('John')
p4 = Person('John')
p1 < p2 #compares id
p1 < p4 # p1 belongs to MITPerson but P4 belongs to Person only. since p1 is used first , tries to compare on ids
p4 < p1 # since p4 belongs Person class both are compared based on Person class comparison
Why does p4 < p1 work, but p1 < p4 doesn’t?
class Student(MITPerson):
pass
class UG(Student):
def __init__(self, name, classYear):
MITPerson.__init__(self, name)
self.year= classYear
def getClass(self):
return self.year
def speak(self, utterance):
return MITPerson.speak(self, " Dude, " + utterance)
class Grad(Student):
pass
class TransferStudent(Student):
pass
def isStudent(obj):
return isinstance(obj,Student)
s1 = UG('Matt Damon', 2017)
s2 = UG('Ben Affleck', 2017)
s3 = UG('Lin Manuel Miranda', 2018)
s4 = Grad('Leonardo di Caprio')
S5 = TransferStudent('Robert deNiro')
print(s1)
print(s1.getClass())
print(s1.speak('where is the quiz?'))
print(s2.speak('I have no clue!'))
class Professor(MITPerson):
def __init__(self, name, department):
MITPerson.__init__(self, name)
self.department= department
def speak(self, utterance):
new = 'In course ' + self.department+ ' we say '
return MITPerson.speak(self, new + utterance)
def lecture(self, topic):
return self.speak('it is obvious that ' + topic)
faculty = Professor('Doctor Arrogent','six')
print(m1.speak('hi there')) # Person instance
print(s1.speak('hi there')) # student instance
print(faculty.speak('hi there')) # Professor instance
print(faculty.lecture('hi there')) # Professor instance
class Grades(object):
"""A mapping from students to a list of grades"""
def __init__(self):
"""Create empty grade book"""
self.students= [] # list of Student objects
self.grades= {} # maps idNum-> list of grades
self.isSorted= True# true if self.studentsis sorted
def addStudent(self, student):
"""Assumes: student is of type Student
Add student to the grade book"""
if student in self.students:
raise ValueError('Duplicate student')
self.students.append(student)
self.grades[student.getIdNum()] = []
self.isSorted= False
def addGrade(self, student, grade):
"""Assumes: grade is a float
Add grade to the list of grades for student"""
try:
self.grades[student.getIdNum()].append(grade)
except KeyError:
raise ValueError('Student not in grade book')
def getGrades(self, student):
"""Return a list of grades for student"""
try: # return copy of student's grades
return self.grades[student.getIdNum()][:]
except KeyError:
raise ValueError('Student not in grade book')
def allStudents(self):
"""Return a list of the students in the grade book"""
if not self.isSorted:
self.students.sort()
self.isSorted= True
return self.students[:]
def gradeReport(course):
"""Assumes: course is of type grades"""
report = []
for s in course.allStudents():
tot = 0.0
numGrades= 0
for g in course.getGrades(s):
tot += g
numGrades+= 1
try:
average = tot/numGrades
report.append(str(s) + '\'s mean grade is '+ str(average))
except ZeroDivisionError:
report.append(str(s) + ' has no grades')
return '\n'.join(report)
#creating students
ug1 = UG('Matt Damon', 2018)
ug2 = UG('Ben Affleck', 2019)
ug3 = UG('Drew Houston', 2017)
ug4 = UG('Mark Zuckerberg', 2017)
g1 = Grad('Bill Gates')
g2 = Grad('Steve Wozniak')
# adding students to course six00
six00 = Grades()
six00.addStudent(g1)
six00.addStudent(ug2)
six00.addStudent(ug1)
six00.addStudent(g2)
six00.addStudent(ug4)
six00.addStudent(ug3)
# adding grades to students
six00.addGrade(g1, 100)
six00.addGrade(g2, 25)
six00.addGrade(ug1, 95)
six00.addGrade(ug2, 85)
six00.addGrade(ug3, 75)
print(Grades.gradeReport(six00))
#update the grades
six00.addGrade(g1, 90)
six00.addGrade(g2, 45)
six00.addGrade(ug1, 80)
six00.addGrade(ug2, 75)
print(Grades.gradeReport(six00)) # Avg if Bill has come down , while Wozniak's has gone up.
Could list all students using
for s in six00.allStudents():
- print(s)
Prints out the list of student names sorted by idNum
Why not just do
for s in six00.students:
- print(s)
Violates the data hiding aspect of an object, and exposes internal representation
def genTest():
yield 1
yield 2
Generators have a next() method which starts/resumes execution of the procedure. Inside of generator:
The difference is that, while a return statement terminates a function entirely, yield statement pauses the function saving all its states and later continues from there on successive calls.
foo = genTest()
foo.__next__()
foo.__next__()
foo.__next__() # throws error after 2 iteration
for n in genTest():
print(n)
def genFib():
fibn_1 = 1 #fib(n-1)
fibn_2 = 0 #fib(n-2)
while True:
# fib(n) = fib(n-1) + fib(n-2)
next = fibn_1 + fibn_2
yield next
fibn_2 = fibn_1
fibn_1 = next
fib = genFib()
fib.__next__()
fib.__next__() # everytime gives next fib number
fib.__next__()
fib.__next__()
fib.__next__()
#BEFORE
def allStudents(self):
if not self.isSorted:
self.students.sort()
self.isSorted= True
return self.students[:]
#return copy of list of students
#AFTER
def allStudents(self):
if not self.isSorted:
self.students.sort()
self.isSorted= True
for s in self.students:
yield s
Reference