博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CS入门学习笔记14-MIT 6.00.1x- Lecture 12 Object Oriented Programming (Class & Generator
阅读量:3938 次
发布时间:2019-05-23

本文共 15488 字,大约阅读时间需要 51 分钟。

Lectrue 12

1. Inheritance

inheritance 指的是不仅要建立class,还要在一个class中建立许多的subclass,让它们都可以共享主class中的standard。

1.1 Using inheritance

  • Let’s build an application that organizes info about people!
    – Person: name, birthday
    • Get last name
    • Sort by last name
    • Get age
import datetimeclass 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 __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 setBirthday(self,month, day, year):        '''sets selfs 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 __str__(self):        """return self's name"""        return self.name

1.2 how sort() work in python

用了一个叫做 __lt__的内置method
在这里插入图片描述1.3 进一步丰富此例子

在这里插入图片描述

class MITPerson(Person): # This is a subclass of Person -- inheritance    nextIdNum = 0 # Next ID number to assign,\    # this is a class attribute, not an attribute for instances        def __init__(self, name):        Person.__init__(self, name) #initialize Person attributes        # new MITPerson attribute: a unique ID number        self.idNum = MITPerson.nextIdNum # idNum is an instance attribute        # 此处idNum需要调用class attribute 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.idNump1 = MITPerson('Eric')p2 = MITPerson('John')p3 = MITPerson('John')p4 = Person('John')>p1 < p2 # 判断哪个在前哪个在后>>> True>p3 < p2>>> False>p4 < p1>>> False>p1 < p4>>>报错 AttributeError"""此处报错的原因在于This __lt__ method "shadows" the Person method, meaning that if we compare an MITPerson object,since its environment inherits from the MITPerson class environment, Python will see this version of __lt__ not the Person version. 但是反过来,在MITPerson中定义的__lt__method,只有局部的视野,并不能看到Person中定义的__lt__,所以调用会报错。下图更清晰地解释了:"""

在这里插入图片描述

1.4 继承的两个好处(from 廖雪峰教程)

  • 获得父类的全部功能

  • 多态

    当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。(此处run仅为一个例子)
    多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思:

对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

对扩展开放:允许新增Animal子类;

对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。

1.5 作业题中启示

  • 关于父类与子类的调用关系:
# -*- coding: utf-8 -*-class Spell(object):    def __init__(self, incantation, name):        self.name = name        self.incantation = incantation    def __str__(self):        return self.name + ' ' + self.incantation + '\n' + self.getDescription()                  def getDescription(self):        return 'No description'        def execute(self):        print self.incantation    class Accio(Spell):    def __init__(self):        Spell.__init__(self, 'Accio', 'Summoning Charm')class Confundo(Spell):    def __init__(self):        Spell.__init__(self, 'Confundo', 'Confundus Charm')    def getDescription(self):        return 'Causes the victim to become confused and befuddled.'def studySpell(spell):    print spellspell = Accio()# note 这里输出Accio 是因为Spell 的 __init__函数里面的参数写反了!# note spell 的 name = Summoning Charm incantation = Acciospell.execute()# note print函数会去调用 __str__ 将__str__返回的结果输出,# note __str__ 函数返回的是  Summoning Charm Accio \nNo description  (\n 是换行符号)studySpell(spell)# note Confundo 是继承自 Spell的,但是它重载override 了父类的函数, getDescription()# note 但是由于他没有重载父类的 __str__ 函数,所以print函数依然会调用 Spell里面的__str__ 函数# note __str__函数里面调用了getDescription函数,因为Confundus自己有getDescription函数,这里会去调用自己的这个函数# note 返回的就是 Confundus Charm Confundo \n Causes the victim to become confused and befuddled.studySpell(Confundo())"""Output:AccioSummoning Charm AccioNo descriptionConfundus Charm ConfundoCauses the victim to become confused and befuddled."""

2.1 进一步丰富前文例子,形成Class Hierarchy结构

# -*- coding: utf-8 -*-import datetimeclass 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 __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 setBirthday(self,month, day, year):        '''sets selfs 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 __str__(self):        """return self's name"""        return self.nameclass MITPerson(Person): # This is a subclass of Person -- inheritance    nextIdNum = 0 # Next ID number to assign,\    # this is a class attribute, not an attribute for instances        def __init__(self, name):        Person.__init__(self, name) #initialize Person attributes        # new MITPerson attribute: a unique ID number        self.idNum = MITPerson.nextIdNum # idNum is an instance attribute        # 此处idNum需要调用class attribute 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.idNumclass UG(MITPerson):    def __init__(self, name, classYear):        MITPerson.__init__(self, name)        self.year = classYear    def getClass(self):        return self.yearclass Grad(MITPerson):    # 这里虽然没有定义任何东西,但因为可以直接继承MITPerson中的method和attribute    # 此处建立grad的原因是为了区别grad和undergraduate两种学生    passdef isStudent(obj):    return isinstance(obj, UG) or isinstance(obj, Grad)

在这里插入图片描述

  • 如果此时需要添加一种transfer student类别,再次进行isStudent校验时就会出现错误,必须要再修改isStudent函数中的判断才行。——> 这时其实最好的办法是改变整个hierarchy,在mitstudent下再建立一个subclass:student,让三种学生都成为student的subclass。如下图所示:

在这里插入图片描述

2.2 Be careful when overriding methods in a subclass!

  • Sbusititution principle
    Important behaviors of superclass should be supported by all subclasses

2.3 在hierarchy中如何调用method以及attribute的取值

class A(object):    def __init__(self):        self.a = 1    def x(self):        print "A.x"    def y(self):        print "A.y"    def z(self):        print "A.z"class B(A):    def __init__(self):        A.__init__(self)        self.a = 2        self.b = 3    def y(self):        print "B.y"    def z(self):        print "B.z"class C(object):    def __init__(self):        self.a = 4        self.c = 5    def y(self):        print "C.y"    def z(self):        print "C.z"class D(C, B):    def __init__(self):        C.__init__(self)        B.__init__(self)        self.d = 6    def z(self):        print "D.z"> obj = D()# 找attribute的顺序:按照代码执行的顺序,从最下面开始,一层一层往上找# 先找D,无a;再找C,a = 4;再找B,B找A,a = 1;再找B自己的,a = 2> print obj.a>>> 2> print obj.b>>> 3> print obj.c>>> 5> print obj.d>>> 6# 找method的顺序:从最下面开始找,找到的第一个便直接执行,不再继续向上寻找> obj.x()>>> A.X> obj.y()>>> C.y> obj.z()>>> D.z

2.4 继续补全上述代码,并形成完整example- A Gradebook

# -*- coding: utf-8 -*-# A Gradebookimport datetimeclass 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# me = Person("William Eric Grimson")# print me# me.getLastName()# me.setBirthday(1,2,1927)# me.getAge()# her = Person("Cher")# her.getLastName()# plist = [me, her]# for p in plist: print p# plist.sort()# for p in plist: print pclass 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# p1 = MITPerson('Eric')# p2 = MITPerson('John')# p3 = MITPerson('John')# p4 = Person('John')# print p1# p1.getIdNum()# p2.getIdNum()# p1 < p2# p3 < p2# p4 < p1# p1 < p4class UG(MITPerson):    def __init__(self, name, classYear):        MITPerson.__init__(self, name)        self.year = classYear    def getClass(self):        return self.yearclass Grad(MITPerson):    passdef isStudent(obj):    return isinstance(obj,UG) or isinstance(obj,Grad)#s1 = UG('Fred', 2016)#s2 = Grad('Angela')#isStudent(s1)#isStudent(s2)class TransferStudent(MITPerson):    pass# go back and define# class Student(MITPerson)# change inheritance for UG, Grad and TransferStudent# change def isStudent(obj):#            return isinstance(obj, Student)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 # grades这个dictionary的key是学生学号,value是一个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()][:] #just make a copy of the grade list 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 studentsdef 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('Jane Doe', 2014)ug2 = UG('John Doe', 2015)ug3 = UG('David Henry', 2003)g1 = Grad('John Henry')g2 = Grad('George Steinbrenner')six00 = Grades()six00.addStudent(g1)six00.addStudent(ug2)six00.addStudent(ug1)six00.addStudent(g2)for s in six00.allStudents(): six00.addGrade(s, 75)"""此处若将代码改写为for s in six00.students: print s是能得出一样的结果的,但是倾向于不这么做,因为this violates the data hiding aspect of an object,and exposes internal representation.-- If I were to change how I want to represent a grade book, I should only need to change the methods within that object, not external procedures that use it"""six00.addGrade(g1, 100)six00.addGrade(g2, 25)six00.addStudent(ug3)# print gradeReport(six00)
3. generator

1. definition

  • Any procedure or method with a yield statement is called a generator
def genTest():    yield 1    yield 2> genTest()>>> 
  • Generators have a next() method which starts/resumes execution of the procedure. Inside of generator(在call generator的next()时可能发生的两种情况):
    yield suspends execution and returns a value
    – Returning from a generator raises a StopIteration exception

next() 的作用是先从程序的开头开始执行,直到reach a “yield” statement,这是会推迟执行,并且返回一个value。

当我们准备好执行接下来的代码时,就再call这个generator的next() function。如果在yield后没有其他语句执行,则会返回一个StopIteration

def genTest():    yield 1    yield 2> foo = genTest()> foo.next()>>> 1 # 返回了第一个yield后面的value> foo.next()>>> 2 # 继续执行了语句,并返回了第二个yield后面的value> foo.next()>>> StopIterationTraceback (most recent call last)
in
()----> 1 foo.next()StopIteration:

2. 用途

2.1 using inside a looping structure

We can use a generator inside a looping structure, as it will continue until it gets a StopIteration exception:

> for n in genTest():      print n>>> 1>>> 2>
# a Fancier example - 计算斐波那契数列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# 每次call genFib_instance.next()都会出现数列中下一个数'''for n in genFib():    print n# 使用此方法可不断打印出此数列中的数,直到键盘中断为止'''

** 2.2 Why Generators**

在这里插入图片描述

在这里插入图片描述** 相比之下,第一种方法要一次性列出所有人,如果学生很多就会非常低效,而第二种方法每次只给出一个学生的名字,更加的efficient。

2.3 generator例子

'''Write a generator, genPrimes, that returns the sequence of prime numbers on successive calls to its next() method: 2, 3, 5, 7, 11, ...如下是两种方法'''def genPrimes():    prime_list = []    next = 1    while True:        next += 1        for p in prime_list:            if next % p == 0:                break        else:            prime_list.append(next)            yield next'''for else 用法:Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the iterable (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement. '''def genPrimes():    primes = [2]    yield primes[0]    guess = 3    while True:        if all(guess%x != 0 for x in primes):            primes.append(guess)                if guess == primes[-1]:            yield primes[-1]        guess += 2'''!!!!注意!在使用generator的时候,要先用函数定义一个instance,再调用next(),而不是直接在generator后面+next。如下方法才是正确的!primeGenerator = genPrimes()primeGenerator.next()'''

转载地址:http://yquwi.baihongyu.com/

你可能感兴趣的文章
解决POJO的属性首字母为大写,但是赋值不了的问题
查看>>
服务器运维整理(笔记)
查看>>
redis分布式锁在MySQL事务代码中使用,没控制好并发原因
查看>>
centos7中的网卡一致性命名规则、网卡重命名方法
查看>>
能切换环境的python
查看>>
Tmux 使用教程
查看>>
DLINK-DSN1100的安装使用记录
查看>>
openssl的学习
查看>>
watchguard ssl100恢复出厂化设置
查看>>
CentOS 一键安装Cacti 1.2.3脚本
查看>>
CentOS 7系统上制作Clonezilla(再生龙)启动U盘并克隆双系统
查看>>
fail2ban的使用-控制连接数
查看>>
btkill-连接数控制
查看>>
NAT+www的发布
查看>>
dhcp.conf
查看>>
关于win10的升级
查看>>
cacti突然不显示流量
查看>>
发现一个好工具记录一下,U盘启动ISO文件。
查看>>
centos7下配置网卡以及查询网卡UUID
查看>>
适用于旧计算机的10款最佳轻量级Linux发行版
查看>>