Lập trình Hướng đối tượng

Lớp và thể hiện của lớp

In [1]:
class Coordinate(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
In [2]:
c = Coordinate(3,4)
origin = Coordinate(0,0)
In [3]:
print(c.x)
print(origin.x)
3
0

Hàm thành phần

In [4]:
class Coordinate(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def distance(self, other):
        x_diff_sq = (self.x-other.x)**2
        y_diff_sq = (self.y-other.y)**2
        return (x_diff_sq + y_diff_sq)**0.5
In [5]:
c = Coordinate(3,4)
origin = Coordinate(0,0)
print(c.distance(origin))
5.0
In [6]:
print(Coordinate.distance(c, origin))
5.0
In [7]:
class Coordinate(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def distance(self, other):
        x_diff_sq = (self.x-other.x)**2
        y_diff_sq = (self.y-other.y)**2
        return (x_diff_sq + y_diff_sq)**0.5
    
    def __str__(self):
        return "<" + str(self.x) + "," + str(self.y) + ">"
In [8]:
c = Coordinate(3,4)
print(c)
<3,4>

check type of an object

In [9]:
print(isinstance(c, Coordinate))
True

Nạp chồng toán tử

In [10]:
class Coordinate(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def distance(self, other):
        x_diff_sq = (self.x-other.x)**2
        y_diff_sq = (self.y-other.y)**2
        return (x_diff_sq + y_diff_sq)**0.5
    
    def __str__(self):
        return "<" + str(self.x) + "," + str(self.y) + ">"
    
    def __sub__(self, other):
        return Coordinate(self.x - other.x, self.y - other.y)
In [11]:
c = Coordinate(3,4)
origin = Coordinate(0,0)
foo = c - origin
print(foo)
<3,4>

Ví dụ: lớp Fraction

In [12]:
class fraction(object):
    def __init__(self, numer, denom):
        self.numer = numer
        self.denom = denom
    
    def __str__(self):
        return str(self.numer) + ' / ' + str(self.denom)
In [13]:
oneHalf = fraction(1, 2)
twoThirds = fraction(2, 3)
In [14]:
print(oneHalf)
print(twoThirds)
1 / 2
2 / 3

Truy xuất các thành phần dữ liệu

In [15]:
class fraction(object):
    def __init__(self, numer, denom):
        self.numer = numer
        self.denom = denom
    
    def __str__(self):
        return str(self.numer) + ' / ' + str(self.denom)
    
    def getNumer(self):
        return self.numer
    
    def getDenom(self):
        return self.denom
In [16]:
oneHalf = fraction(1, 2)
twoThirds = fraction(2, 3)
In [17]:
oneHalf.getNumer()
Out[17]:
1
In [18]:
fraction.getDenom(twoThirds)
Out[18]:
3

Nạp chồng toán tử + và -

In [19]:
class fraction(object):
    def __init__(self, numer, denom):
        self.numer = numer
        self.denom = denom
    
    def __str__(self):
        return str(self.numer) + ' / ' + str(self.denom)
    
    def getNumer(self):
        return self.numer
    
    def getDenom(self):
        return self.denom
    
    def __add__(self, other):
        numerNew = other.getDenom() * self.getNumer() \
                   + other.getNumer() * self.getDenom()
        denomNew = other.getDenom() * self.getDenom()
        return fraction(numerNew, denomNew)
    
    def __sub__(self, other):
        numerNew = other.getDenom() * self.getNumer() \
                   - other.getNumer() * self.getDenom()
        denomNew = other.getDenom() * self.getDenom()
        return fraction(numerNew, denomNew)
In [20]:
oneHalf = fraction(1, 2)
twoThirds = fraction(2, 3)
In [21]:
new = oneHalf + twoThirds
print(new)
7 / 6
In [22]:
threeQuarters = fraction(3, 4)
secondNew = twoThirds - threeQuarters
print(secondNew)
-1 / 12

Chuyển đổi kiểu dữ liệu

In [23]:
class fraction(object):
    def __init__(self, numer, denom):
        self.numer = numer
        self.denom = denom
    
    def __str__(self):
        return str(self.numer) + ' / ' + str(self.denom)
    
    def getNumer(self):
        return self.numer
    
    def getDenom(self):
        return self.denom
    
    def __add__(self, other):
        numerNew = other.getDenom() * self.getNumer() \
                   + other.getNumer() * self.getDenom()
        denomNew = other.getDenom() * self.getDenom()
        return fraction(numerNew, denomNew)
    
    def __sub__(self, other):
        numerNew = other.getDenom() * self.getNumer() \
                   - other.getNumer() * self.getDenom()
        denomNew = other.getDenom() * self.getDenom()
        return fraction(numerNew, denomNew)
    
    def convert(self):
        return self.getNumer() / self.getDenom()
In [24]:
oneHalf = fraction(1, 2)
oneHalf.convert()
Out[24]:
0.5

Ví dụ: Lớp chứa tập các số nguyên

In [25]:
class intSet(object):
    def __init__(self):
        self.vals = []
    
    def insert(self, e):
        if not e in self.vals:
            self.vals.append(e)
    
    def member(self, e):
        return e in self.vals
    
    def remove(self, e):
        try:
            self.vals.remove(e)
        except:
            raise ValueError(str(e) + ' not found')
    
    def __str__(self):
        self.vals.sort()
        result = ''
        for e in self.vals:
            result = result + str(e) + ','
        return '{' + result[:-1] + '}'
In [26]:
s = intSet()
print(s)
{}
In [27]:
s.insert(3)
s.insert(4)
s.insert(3)
print(s)
{3,4}
In [28]:
s.member(3)
Out[28]:
True
In [29]:
s.member(5)
Out[29]:
False
In [30]:
s.insert(6)
print(s)
{3,4,6}
In [31]:
s.remove(3)
print(s)
{4,6}
In [32]:
s.remove(3)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-25-ae5ab2e4f873> in remove(self, e)
     13         try:
---> 14             self.vals.remove(e)
     15         except:

ValueError: list.remove(x): x not in list

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-32-b06e0f5ff900> in <module>()
----> 1 s.remove(3)

<ipython-input-25-ae5ab2e4f873> in remove(self, e)
     14             self.vals.remove(e)
     15         except:
---> 16             raise ValueError(str(e) + ' not found')
     17 
     18     def __str__(self):

ValueError: 3 not found

Vì sao OOP

In [33]:
class Animal(object):
    def __init__(self, age):
        self.age = age
        self.name = None
In [34]:
myAnimal = Animal(3)

Hàm getter và setter

In [35]:
class Animal(object):
    def __init__(self, age):
        self.age = age
        self.name = None
    
    def get_age(self):
        return self.age
    
    def get_name(self):
        return self.name
    
    def set_age(self, newage):
        self.age = newage
    
    def set_name(self, newname=""):
        self.name = newname
    
    def __str__(self):
        return "animal:"+str(self.name)+":"+str(self.age)
In [36]:
myAnimal = Animal(3)
print(myAnimal)
animal:None:3
In [37]:
myAnimal.set_name('foobar')
print(myAnimal)
animal:foobar:3
In [38]:
myAnimal.get_age()
Out[38]:
3
In [39]:
myAnimal.age
Out[39]:
3

Che dấu thông tin

In [40]:
class Animal(object):
    def __init__(self, age):
        self.years = age
        
    def get_age(self):
        return self.years

Phân cấp lớp - Thừa kế

In [41]:
class Animal(object):
    def __init__(self, age):
        self.age = age
        self.name = None
    
    def get_age(self):
        return self.age
    
    def get_name(self):
        return self.name
    
    def set_age(self, newage):
        self.age = newage
    
    def set_name(self, newname=""):
        self.name = newname
    
    def __str__(self):
        return "animal:"+str(self.name)+":"+str(self.age)
In [42]:
class Cat(Animal):
    def speak(self):
        print('meow')
        
    def __str__(self):
        return "cat:"+str(self.name)+":"+str(self.age)
In [43]:
jelly = Cat(1)
jelly.get_name()
In [44]:
jelly.set_name('JellyBelly')
jelly.get_name()
Out[44]:
'JellyBelly'
In [45]:
print(jelly)
cat:JellyBelly:1
In [46]:
print(Animal.__str__(jelly))
animal:JellyBelly:1
In [47]:
blob = Animal(1)
print(blob)
animal:None:1
In [48]:
blob.set_name()
print(blob)
animal::1
In [49]:
class Rabbit(Animal):
    def speak(self):
        print("meep")
    
    def __str__(self):
        return "rabbit:"+str(self.name)+":"+str(self.age)
In [50]:
peter = Rabbit(5)
jelly.speak()
meow
In [51]:
peter.speak()
meep
In [52]:
blob.speak()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-52-94bb5e7b33ae> in <module>()
----> 1 blob.speak()

AttributeError: 'Animal' object has no attribute 'speak'
In [53]:
class Person(Animal):
    def __init__(self, name, age):
        Animal.__init__(self, age)
        Animal.set_name(self, name)
        self.friends = []
    
    def get_friends(self):
        return self.friends
    
    def add_friend(self, fname):
        if fname not in self.friends:
            self.friends.append(fname)
    
    def speak(self):
        print("hello")
    
    def age_diff(self, other):
        # alternate way: diff = self.age - other.age
        diff = self.get_age() - other.get_age()
        if self.age > other.age:
            print(self.name, "is", diff, "years older than", other.name)
        else:
            print(self.name, "is", -diff, "years younger than", other.name)
    
    def __str__(self):
        return "person:"+str(self.name)+":"+str(self.age)
In [54]:
eric = Person('eric', 45)
john = Person('john', 55)
In [55]:
eric.speak()
hello
In [56]:
eric.age_diff(john)
eric is 10 years younger than john
In [57]:
Person.age_diff(john, eric)
john is 10 years older than eric
In [58]:
import random

class Student(Person):
    def __init__(self, name, age, major=None):
        Person.__init__(self, name, age)
        self.major = major
    
    def change_major(self, major):
       self.major = major
    
    def speak(self):
        r = random.random()
        if r < 0.25:
            print("i have homework")
        elif 0.25 <= r < 0.5:
            print("i need sleep")
        elif 0.5 <= r < 0.75:
            print("i should eat")
        else:
            print("i am watching tv")
    
    def __str__(self):
        return "student:"+str(self.name)+":"+str(self.age)+":"+str(self.major)
In [59]:
fred = Student('fred', 18, 'Course VI')
print(fred)
student:fred:18:Course VI
In [60]:
fred.speak()
i need sleep
In [61]:
fred.speak()
i have homework
In [62]:
fred.speak()
i am watching tv
In [63]:
fred.speak()
i have homework

Biến lớp - class variable

In [64]:
class Animal(object):
    def __init__(self, age):
        self.age = age
        self.name = None
    
    def get_age(self):
        return self.age
    
    def get_name(self):
        return self.name
    
    def set_age(self, newage):
        self.age = newage
    
    def set_name(self, newname=""):
        self.name = newname
    
    def __str__(self):
        return "animal:"+str(self.name)+":"+str(self.age)

class Cat(Animal):
    def speak(self):
        print("meow")
    
    def __str__(self):
        return "cat:"+str(self.name)+":"+str(self.age)
In [65]:
class Rabbit(Animal):
    tag = 1
    
    def __init__(self, age, parent1=None, parent2=None):
        Animal.__init__(self, age)
        self.parent1 = parent1
        self.parent2 = parent2
        self.rid = Rabbit.tag
        Rabbit.tag += 1
    
    def get_rid(self):
        return str(self.rid).zfill(3)
    
    def get_parent1(self):
        return self.parent1
    
    def get_parent2(self):
        return self.parent2
    
    def __add__(self, other):
        # returning object of same type as this class
        return Rabbit(0, self, other)
    
    def __eq__(self, other):
        parents_same = self.parent1.rid == other.parent1.rid \
                       and self.parent2.rid == other.parent2.rid
        parents_opposite = self.parent2.rid == other.parent1.rid \
                       and self.parent1.rid == other.parent2.rid
        return parents_same or parents_opposite
In [66]:
peter = Rabbit(2)
peter.set_name('Peter')
hopsy = Rabbit(3)
hopsy.set_name('Hopsy')
cotton = Rabbit(1, peter, hopsy)
cotton.set_name('Cottontail')
print(cotton)
animal:Cottontail:1
In [67]:
print(cotton.get_parent1())
animal:Peter:2
In [68]:
mopsy = peter + hopsy
mopsy.set_name('Mopsy')
print(mopsy.get_parent1())
animal:Peter:2
In [69]:
print(mopsy.get_parent2())
animal:Hopsy:3
In [70]:
print(mopsy == cotton)
True

Ví dụ

Class Person

In [71]:
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 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 ame is lexicographically
           less than other's name, and False otherwise"""
        if self.lastName == other.lastName:
            return self.name < other.name
        return self.lastName < other.lastName
    
    def __str__(self):
        """return self's name"""
        return self.name
In [72]:
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]
In [73]:
print(p1)
Mark Zuckerberg
In [74]:
for e in personList:
    print(e)
Mark Zuckerberg
Drew Houston
Bill Gates
Andrew Gates
Steve Wozniak
In [75]:
personList.sort()
for e in personList:
    print(e)
Andrew Gates
Bill Gates
Drew Houston
Steve Wozniak
Mark Zuckerberg

Lớp MITPerson

In [76]:
class MITPerson(Person):
    nextIdNum = 0 # next ID number to assign

    def __init__(self, name):
        Person.__init__(self, name) # initialize Person attributes
        # new MITPerson attribute: a unique ID number
        self.idNum = MITPerson.nextIdNum
        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)
In [77]:
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]
In [78]:
print(m1)
Bill Gates
In [79]:
print(m1.speak('hi there'))
Gates says: hi there
In [80]:
for e in MITPersonList:
    print(e)
Bill Gates
Drew Houston
Mark Zuckerberg
In [81]:
MITPersonList.sort()

print()

for e in MITPersonList:
    print(e)
Mark Zuckerberg
Drew Houston
Bill Gates
In [82]:
p1 =  MITPerson('Eric')
p2 = MITPerson('John')
p3 = MITPerson('John')
p4 = Person('John')
In [83]:
p1<p2
Out[83]:
True
In [84]:
p1<p4
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-84-e4b6153c0891> in <module>()
----> 1 p1<p4

<ipython-input-76-fa92103c453e> in __lt__(self, other)
     13     # sorting MIT people uses their ID number, not name!
     14     def __lt__(self, other):
---> 15         return self.idNum < other.idNum
     16 
     17     def speak(self, utterance):

AttributeError: 'Person' object has no attribute 'idNum'
In [85]:
p4<p1
Out[85]:
False

Các lớp Student

In [86]:
class UG(MITPerson):
    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(MITPerson):
    pass

def isStudent(obj):
    return isinstance(obj,UG) or isinstance(obj,Grad)
In [87]:
s1 = UG('Matt Damon', 2017)
s2 = UG('Ben Affleck', 2017)
s3 = UG('Lin Manuel Miranda', 2018)
s4 = Grad('Leonardo di Caprio')

studentList = [s1, s2, s3, s4]
In [88]:
print(s1)
Matt Damon
In [89]:
print(s1.getClass())
2017
In [90]:
print(s1.speak('where is the quiz?'))
Damon says:  Dude, where is the quiz?
In [91]:
print(s2.speak('I have no idea'))
Affleck says:  Dude, I have no idea
In [92]:
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)
In [93]:
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')

studentList = [s1, s2, s3, s4, s5]
In [94]:
print(s1)
print(s1.getClass())
print(s1.speak('where is the quiz?'))
print(s2.speak('I have no clue!'))
Matt Damon
2017
Damon says:  Dude, where is the quiz?
Affleck says:  Dude, I have no clue!

Lớp Professor

In [95]:
class Professor(MITPerson):
    def __init__(self, name, department):
        MITPerson.__init__(self, name)
        self.department = department
        
    def speak(self, utterance):
        newUtterance = 'In course ' + self.department + ' we say '
        return MITPerson.speak(self, newUtterance + utterance)
        
    def lecture(self, topic):
        return self.speak('it is obvious that ' + topic)
In [96]:
faculty = Professor('Doctor Arrogant', 'six')
In [97]:
print(m1.speak('hi there'))
Gates says: hi there
In [98]:
print(s1.speak('hi there'))
Damon says:  Dude, hi there
In [99]:
print(faculty.speak('hi there'))
Arrogant says: In course six we say hi there
In [100]:
print(faculty.lecture('hi there'))
Arrogant says: In course six we say it is obvious that hi there
In [101]:
class MITPerson(Person):
    nextIdNum = 0 # next ID number to assign

    def __init__(self, name):
        Person.__init__(self, name) # initialize Person attributes
        # new MITPerson attribute: a unique ID number
        self.idNum = MITPerson.nextIdNum
        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.name + " says: " + utterance)
In [102]:
print(m1.speak('hi there'))
print(s1.speak('hi there'))
print(faculty.speak('hi there'))
print(faculty.lecture('hi there'))
Gates says: hi there
Matt Damon says:  Dude, hi there
Doctor Arrogant says: In course six we say hi there
Doctor Arrogant says: In course six we say it is obvious that hi there
In [103]:
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, " Yo Bro, " + utterance)

class Grad(Student):
    pass

class TransferStudent(Student):
    pass

def isStudent(obj):
    return isinstance(obj,Student)
In [104]:
print(m1.speak('hi there'))
print(s1.speak('hi there'))
print(faculty.speak('hi there'))
print(faculty.lecture('hi there'))
Gates says: hi there
Matt Damon says:  Dude, hi there
Doctor Arrogant says: In course six we say hi there
Doctor Arrogant says: In course six we say it is obvious that hi there

Ví dụ GradeBook

In [105]:
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.students is 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[:] 
        #return copy of list of students
In [106]:
def gradeReport(course):
    """Assumes: course if 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)
In [107]:
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')

six00 = Grades()
six00.addStudent(g1)
six00.addStudent(ug2)
six00.addStudent(ug1)
six00.addStudent(g2)
six00.addStudent(ug4)
six00.addStudent(ug3)


six00.addGrade(g1, 100)
six00.addGrade(g2, 25)
six00.addGrade(ug1, 95)
six00.addGrade(ug2, 85)
six00.addGrade(ug3, 75)

print()

print(gradeReport(six00))
Matt Damon's mean grade is 95.0
Ben Affleck's mean grade is 85.0
Drew Houston's mean grade is 75.0
Mark Zuckerberg has no grades
Bill Gates's mean grade is 100.0
Steve Wozniak's mean grade is 25.0
In [108]:
# thêm điểm mới
six00.addGrade(g1, 90)
six00.addGrade(g2, 45)
six00.addGrade(ug1, 80)
six00.addGrade(ug2, 75)

print()

print(gradeReport(six00))
Matt Damon's mean grade is 87.5
Ben Affleck's mean grade is 80.0
Drew Houston's mean grade is 75.0
Mark Zuckerberg has no grades
Bill Gates's mean grade is 95.0
Steve Wozniak's mean grade is 35.0
In [109]:
for s in six00.allStudents():
    print(s)
Matt Damon
Ben Affleck
Drew Houston
Mark Zuckerberg
Bill Gates
Steve Wozniak

Generator

In [110]:
def genTest():
    yield 1
    yield 2
    
foo = genTest()

foo.__next__()
Out[110]:
1
In [111]:
foo.__next__()
Out[111]:
2
In [112]:
foo.__next__()
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-112-5d994c17f9ca> in <module>()
----> 1 foo.__next__()

StopIteration: 
In [113]:
for n in genTest():
    print(n)
1
2
In [114]:
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
In [115]:
fib = genFib()

fib.__next__()
Out[115]:
1
In [116]:
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.students is 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
        for s in self.students:
            yield s
        
def gradeReport(course):
    """Assumes: course if 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)

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')

six00 = Grades()
six00.addStudent(g1)
six00.addStudent(ug2)
six00.addStudent(ug1)
six00.addStudent(g2)
six00.addStudent(ug4)
six00.addStudent(ug3)

six00.addGrade(g1, 100)
six00.addGrade(g2, 25)
six00.addGrade(ug1, 95)
six00.addGrade(ug2, 85)
six00.addGrade(ug3, 75)

print()

print(gradeReport(six00))
Matt Damon's mean grade is 95.0
Ben Affleck's mean grade is 85.0
Drew Houston's mean grade is 75.0
Mark Zuckerberg has no grades
Bill Gates's mean grade is 100.0
Steve Wozniak's mean grade is 25.0