python 学习简记 《编程导论》 CH8&CH9

来源:互联网 发布:js触发事件消失 编辑:程序博客网 时间:2024/05/21 09:12

《编程导论》 CH8 类和面向对象编程

8.1 抽象的数据类型和类

抽象的数据类型vs接口

降低改动程序的复杂度->解耦&抽象

Python中使用类来实现数据抽象

例子:intSet类的定义来实现一个简单的整数集合抽象

class IntSet(object):                                      #类开头是class关键字;IntSet是object的子类    """intSet is a group of integer"""    def __init__(self):                    #类被实例化是会调用类中定义的__init__方法,init前后各两根_        """create a blank int list"""        self.vals = []    def insert(self,e):        """assume e is an integer, insert e into self"""        if not e in self.vals:            self.vals.append(e)    def member(self,e):        """assume e is an integer,                if e in self return true, else return false"""        return e in self.vals    def remove(self,e):        """assume e is an integer, delete e from self,                if e is not in self throw ValueError exception"""        try:            self.vals.remove(e)        except:            raise ValueError(str(e) + ' not found')    def getMember(self):        """return a list which contains elements in self.                the order of the elements cannot be assumed."""        return self.val[:]    def __str__(self):                             #当使用print语句时,和类关联的__str__函数会被自动调用        """return the string form of self"""        self.vals.sort()        result = ''        for e in self.vals:            result = result + str(e) + ','        return '{'+result[:-1]+'}' #-1 omits trailing comma

定义一个类会创建一个type类型的对象并给它关联一些instancemethod(实例方法)类型的对象。如,表达式IntSet.insert引用的是IntSet类中定义的insert方法。

代码: print type(IntSet),type(IntSet.insert)

会输出: <type 'type'> <type 'instancemethod'>

类中出现函数定义,这个函数会被称为方法并和这个类关联起来。->方法属性 vs 数据属性(vals)

类支持两类操作:

实例化:用来创建类的实例。如语句S=IntSet()会创建一个类型为IntSet的新对象,此新对象称为IntSet的一个实例。

属性引用:使用点标记法来访问类所关联的属性。如,s.member指的是IntSet的实例s所关联的方法member。

可以使用点标记法来调用和类实例关联的方法,如:

s=IntSet()s.insert(3)print s.member(s)

表达式中点号前面的对象会被当作第一个参数传入方法。惯例用self来命名绑定到这个实参的形参。


不应把类和类的实例搞混。属性既可以关联到类本身,也可以关联到类的实例:

方法属性定义在类定义中,如IntSet.member是类IntSet的一个属性。当类被实例化时,例如通过s=IntSet(),实例的属性(如s.member)会被创建。IntSet.member和s.member是不同的对象。在IntSet类的定义中,s.member最初被绑定到member方法上,但是在计算的过程中可以改变这一绑定。

当数据属性被关联到类时称为类变量,被关联到实例时称为实例变量。


表现的独立性->一个抽象类型的实现包含:

·类型方法的实现

·用来表示类型值的数据结构

·关于方法如何使用数据结构的约定->表示的不变性->定义如何通过数据属性表示合法的抽象值


1.使用抽象的数据类型来设计程序

数据抽象鼓励程序设计者关注数据对象的中心性,而非关注函数。

2.使用类来记录学生和教师

import datetimeclass Person(object):    def __init__(self,name):        """create a preson"""        self.name = name        try:            lastBlank = name.rindex(' ')            self.lastName = name[lastBlank+1:]        except:            self.lastName = name        self.birthday = None    def getName(self):        """return the person's name"""        return self.name    def getLastName(self):        """return the person's last name"""        return self.lastName    def setBirthday(self,birthDate):        """assume the type of birthDate is datetime.date                set the person's birthday as birthDate"""        self.birthday = birthDate    def getAge(self):        """return the relevant number of days of the person's age"""        if self.birthday == None:            raise ValueError        return (datetime.date.today()-self.birthday).days    def __lt__(self,other):  #重载小于符号        """if the dictionary order of the person's name is less than the other's return true,                else return false"""        if self.lastName == other.lastName:            return self.name < other.lastName        return self.lastName < other.lastName    def __str__(self):        """return the person's name"""        return self.nameme = Person('Michael Guttag')him = Person('Barack Hussein Obama')her = Person('Madonna')print him.getLastName()him.setBirthday(datetime.date(1961,8,4))her.setBirthday(datetime.date(1958,8,16))print him.getName(),'is',him.getAge(),'days old'pList = [me,him,her]for p in pList:    print ppList.sort()for p in pList:    print p

8.2 继承

class MITPerson(Person):    nextIdNum = 0 #id number        def __init__(self,name):        Person.__init__(self,name)        self.idNum = MITPerson.nextIdNum        MITPerson.nextIdNum += 1            def getIdNum(self):        return self.idNum        def __lt__(self, other):        return self.idNum < other.idNum

子类(MITPerson)继承了超类(Person)的属性,子类可以:

·添加新属性

·覆盖超类的属性

1. 多层继承

Python使用保留字pass作为类的代码部分,这个类中只有从超类继承来的属性。

class Student(MITPerson):    passclass UG(Student):    def __init__(self,name,classYear):        MITPerson.__init__(self,name)        self.year = classYear    def getClass(self):        return self.yearclass Grad(Student):    passp5=Grad('Buzz Aldrin')p6=('Billy Beaver',1984)print p5,'is a graduate student is',type(p5) == Gradprint p5,'is an undergraduate student is',type(p5)==UG
结合上面的类,继承关系可以如下描述:

Person-MITPerson-Student-UG

                                            \ G

中间类型Student的作用很巧妙。可以向MITPerson类添加下面的方法:

def isStudent(self):    return isinstance(self,Student)
isinstance是Python的内建函数。isinstance的第一参数可以是任意对象,但是第二个参数必须是type类型的对象。当且仅当第一个参数是第二个参数的实例时,函数返回True。如isinstance([1,2],list)的值是true。


替代法则

       使用子类来定义类型的层次结构时,子类型应当被看作父类型行为的拓展。可以通过添加新属性或者覆盖继承自超类的属性来实现。

       子类覆盖父类的方法时要小心,超类的重要行为一定要被所有子类支持。如果用户的代码使用超类的一个实例能正确运行,那么使用子类的实例替换掉超类的实例之后仍然应可以正常工作。


8.3 封装和信息隐藏

      下面的代码可以用来记录一组学生的成绩。

class Grades(object):    def __init__(self):        self.students=[]        self.grades={}        self.isSorted=True    def addStudent(self,student):        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):        try:            self.grades[student.getIdNum()].append(grade)        except:            raise ValueError('Student not in mapping')            def getGrades(self,student):        try:            return self.grades[student.getIdNum()][:] #返回成绩副本,避免意料之外的改动        except:            raise ValueError('Student not in mapping')            def getStudents(self):        if not self.isSorted:            self.students.sort()            self.isSorted = True        return self.students[:]

下面的函数使用Grades类生成6.00这门课的学生成绩列表。

def gradeReport(course):    report = ''    for s in course.getStudents():        tot=0.0        numGrades = 0        for g in course.getGrades(s):            tot+=g            numGrades += 1        try:            average = tot/numGrades            report = report + '\n'  +str(s)+'\'s mean grade is '+str(average)        except ZeroDivisionError:            report = report + '\n'+str(s)+' has no grades'    return reportug1 =UG('Jane Doe',2014)ug2 =UG('John Doe',2015)ug3 =UG('David Henry',2003)g1 =Grad('Billy Bucker')g2 =Grad('Bucky F.Dent')sixHundred=Grades()sixHundred.addStudent(ug1)sixHundred.addStudent(ug2)sixHundred.addStudent(g1)sixHundred.addStudent(g2)for s in sixHundred.getStudents():    sixHundred.addGrade(s,75)sixHundred.addGrade(g1,25)sixHundred.addGrade(g2,100)sixHundred.addStudent(ug3)print gradeReport(sixHundred)

面向对象编程的两个核心概念:

封装:封装是指将数据属性和操作它们的方法捆绑在一起。如:Rafael=MITPerson() 可以使用点标记法来访问Rafael的属性,比如年龄和学号。

信息隐藏:这是模块化的关键。Java&C++等编程语言提供了可以强制隐藏信息的方法。程序猿可以控制类中数据属性的可见性,因此可以强制要求用户代码只能通过对象的属性来访问数据。然而Python没有提供强制隐藏信息的方法。因此无法严格地控制类实例属性的访问。

1.生成器

数据隐藏->性能损失

看上面代码中的gradeReport。调用course.allStudents会创建并返回一个大小为n的列表,n代表学生数量。创建一个已有列表的副本非常低效。一种方法是放弃抽象并允许gradeReport直接访问实例变量course.students,但是这会破坏信息隐藏。幸好还有另一种方法:yield

改进版getStudents:

def getStudents(self):        if not self.isSorted:            self.students.sort()            self.isSorted=True        for s in self.students:            yield s

包含yield语句的函数定义会被特殊对待。yield语句会告诉Python函数是一个生成器。生成器通常和for语句一起使用。(要真正理解生成器,需要理解Python内建迭代器的实现方法)。

在for循环的第一次迭代开始时,解释器会执行生成器内部的代码。它会运行到第一个yield语句然后返回yield语句中表达式的值。在下一次迭代时,生成器会从上次返回的位置继续执行,本地变量的绑定也和上次执行时一样。解释器会一直运行到下一个yield语句然后再次跳出。循环会在执行完所有代码或者遇到return语句时退出。

上面的getStudents可以让程序员用for循环来遍历Grades类型对象中的所有学生,就像遍历列表等内建类型的元素一样。举例来说,代码:

book = Grades()book.addStudent(Grad('Julie'))book.addStudent(Grad('Charlie'))for s in book.getStudents():     print s
会输出:

Julie
Charlie

无论getStudents是一次返回一个列表还是一次生成一个值,for循环都可以进行遍历。一次生成一个值效率更高,因为这样不需要创建新列表。


8.4 进阶实例:抵押贷款

下面的代码比较了三种不同类型的抵押贷款

__author__ = 'sunzhaoyue'def findPayment(loan,r,m):    """assume:loan and r are float,m is an integer    return the money should be refunded when the num of money loaned is loan,    monthly interest is r in total m months"""    return loan*((r*(1+r)**m)/((1+r)**m-1))class Mortgage(object):    """an abstract class to construct different types of mortgage"""    def __init__(self,loan,annRate,months):        self.loan = loan        self.rate = annRate/12.0        self.months = months        self.paid = [0.0]        self.owed = [loan]        self.payment = findPayment(loan,self.rate,months)        self.legend = None #description of mortage    def makePayment(self):        """refund"""        self.paid.append(self.payment)        reduction = self.payment - self.owed[-1]*self.rate        self.owed.append(self.owed[-1] - reduction)    def getTotalPaid(self):        """return num of money has been repaid now"""        return sum(self.paid)    def __str__(self):        return self.legendclass Fixed(Mortgage):    def __init__(self,loan,r,months):        Mortgage.__init__(self,loan,r,months)        self.legend = 'Fixed, '+str(r*100)+ '%'class FixedWithPts(Mortgage):    def __init__(self,loan,r,months,pts):        Mortgage.__init__(self,loan,r,months)        self.pts = pts        self.paid = [loan*(pts/100.0)]        self.legend = 'Fixed, '+str(r*100)+'%, '+str(pts)+' points'class TwoRate(Mortgage):    def __init__(self,loan,r,months,teaserRate,teaserMonths):        Mortgage.__init__(self,loan,teaserRate,months)        self.teaserMonths = teaserMonths        self.teaserRate = teaserRate        self.nextRate = r/12.0        self.legend = str(teaserRate*100) + '% for'+str(self.teaserMonths)+' months, then'+str(r*100)+'%'    def makePayment(self):        if len(self.paid)==self.teaserMonths+1:            self.rate = self.nextRate            self.payment = findPayment(self.owed[-1],self.rate,self.months-self.teaserMonths)        Mortgage.makePayment(self)def compareMortgages(amt,years,fixedRate,pts,ptsRate,varRate1,varRate2,varMonths):    totMonths = years*12    fixed1 = Fixed(amt, fixedRate, totMonths)    fixed2 = FixedWithPts(amt, ptsRate, totMonths, pts)    twoRate = TwoRate(amt, varRate2, totMonths, varRate1, varMonths)    morts = [fixed1,fixed2,twoRate]    for m in range(totMonths):        for mort in morts:            mort.makePayment()    for m in morts:        print m        print ' Total payments = $' + str(int(m.getTotalPaid()))compareMortgages(amt=200000, years=30, fixedRate=0.07, pts=3.25,                 ptsRate=0.05, varRate1=0.045, varRate2=0.095, varMonths=48)

执行代码会输出:

Fixed, 7.0%
 Total payments = $479017
Fixed, 5.0%, 3.25 points
 Total payments = $393011
4.5% for48 months, then9.5%
 Total payments = $551444


CH9   算法复杂度简介


9.1 思考计算复杂度

时间的抽象、解决对输入的依赖

最好情况、最坏情况、平均情况

最坏情况定义了运行时间的上界

9.2 渐进表示

为了量化“大规模”,我们使用渐进表示来描述当输入规模接近无限大时,算法的复杂度。

Big O表示法

9.3 一些重要的复杂度

1.常数复杂度 O(1)

表示渐近复杂度和输入的规模无关。

2.对数复杂度O(log n) 二分查找

3.线性复杂度O(n)  

4.对数线性复杂度 O(n log(n)) 归并排序

5.多项式复杂度

6.指数复杂度

7.复杂度对比


这部分由于数据结构都有介绍,有些东西都很熟悉了,就简单记下

0 0
原创粉丝点击