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.复杂度对比
这部分由于数据结构都有介绍,有些东西都很熟悉了,就简单记下
- python 学习简记 《编程导论》 CH8&CH9
- python 学习简记《编程导论》CH2&CH3
- python 学习简记 《编程导论》 CH4&CH5
- python 学习简记 《编程导论》 CH6&CH7
- python 学习简记 《编程导论》 CH10&CH11
- Python学习简记
- python学习手册 简记
- python学习简记02
- 算法导论 ch9 最大值与最小值
- Python教程学习简记3--Python 切片
- 计算机科学与Python编程导论_MIT 学习笔记(一)
- 计算机科学与Python编程导论_MIT 学习笔记(二)
- 计算机科学与Python编程导论_MIT 学习笔记(三)
- 计算机科学与Python编程导论_MIT 学习笔记(四)
- 计算机科学与Python编程导论_MIT 学习笔记(五)
- 计算机科学与Python编程导论_MIT 学习笔记(六)
- Python简记
- 学习简记
- 自定义slideMenu
- 数据库范式解析
- Ubuntu中解压和压缩命令
- SharedPreference
- bzoj3289 树状数组加莫队注意这题每天的交换该是不影响的。。。还有一次交换等价于去掉一个逆序
- python 学习简记 《编程导论》 CH8&CH9
- 机器学习 第一讲
- js(七)——with函数
- 美国HOLADAY HI2200宽频电磁辐射检测仪维修实例
- Iframe 用法的详细讲解
- demo/regLogin核心代码分析
- xlistview导入文件1
- Center
- PAT(乙级)1027. 打印沙漏