Python 编程导论 Chapter 8 —— 类与面向对象编程


typora-copy-images-to: Python 编程导论

文章目录

    • typora-copy-images-to: Python 编程导论
  • Python 编程导论 Chapter 8 —— 类与面向对象编程
      • 8.1 抽象数据类型与类
        • 8.1.1 使用抽象数据类型设计程序
        • 8.1.2 使用类记录学生与教师
      • 8.2 继承
        • 8.2.1 多重继承
        • 8.2.2 替换原则
      • 8.3 封装与信息隐藏
      • 8.4 进阶示例:抵押贷款

Python 编程导论 Chapter 8 —— 类与面向对象编程


  • 重点是面向对象编程的环境下使用类,面向对象编程的关键,是将对象看做是数据和可以在数据上执行的方法的集合
  • Python 的对象都有类型,定义了程序能够在这个对象上执行的操作

8.1 抽象数据类型与类

  • 抽象数据类型,是指由对象以及对象上的操作组成的集合,捆绑为一个整体,可以传递到程序的另一个部分,可以使用对象数据属性以及使用对象上的操作

  • 目的,使程序易于修改,控制程序复杂程度,两种机制可以完成这个任务:

    • 分解,使程序具有结构
    • 抽象,隐藏细节
  • Python语言中,使用实现数据抽象,以下是一个类定义,实现对整数集合的抽象:

class IntSet(object):
    """IntSet是一个整数集合"""
    # 关于实现的信息
    # 集合的值由一个整合组self.vals表示
    # 集合中的每个整数在self.vals中只出现一次
    def__init__(self):
        """创建一个空的整数集合"""
        self.vals = []
    
    def insert(self,e):
        if e not in self.vals:
            self.vals.append(e)
            
    def member(self,e):
        """假设e是整数
           如果e在self中,则返回True,否则返回False"""
        return e in self.vals
    
    def remove(self,e):
        """假设e是整数,从self中删除e
		  如果e不在self中,则抛出ValueError异常"""
        try:
            self.vals.remove(e)
        except:
            raise ValueError(str(e) + ' not found')
            
            
    def getMembers(self):
        """返回一个包含self中元素的列表
		  对元素不进行排序"""
        return self.vals[:]
    
    def __str__(self):
        """返回一个表示self的字符串"""
        self.vals.sort()
        result = ""
        for e in self.vals:
            result = result + str(e) + ","
        return '{' + result[:-1] + '}'
        
s = IntSet()
s.insert(3)
print(s.member(3))        

# 以上代码可以创建一个实例,并在这个IntSet中插入整数3,然后输出True
  • 类支持两种操作:

    • 实例化:创建类的实例 例如:s = IntSet()
    • 属性引用:通过点标记法访问与类关联的属性,如:s.member
  • Python 中的特殊方法名,__ init__,只要一个类被实例化,就会调用该类中定义的__ init __方法

  • 方法属性定义在类定义中,类被实例化后,实例属性才被创建

  • 数据属性被关联到类时,称其为类变量,数据属性被关联到实例时,将其称为实例变量

  • 表示不变性定义了数据属性中的哪个值对应着类实例的有效表示

8.1.1 使用抽象数据类型设计程序

  • 程序是类型的集合,替代以函数为中心的理念
  • 数据抽象一般会比单个函数包含更多功能

8.1.2 使用类记录学生与教师

  • 应该先仔细思考可能有用的抽象,是否有一种抽象可以覆盖学生、教授、职员的常用属性?
  • Python 标准库模块datetime,提供了很多创建和处理日期数据的方法
import datetime
class Person(object):
	def __init__(self, name):
		"""创建一个人"""
		self.name = name
		try:
			lastBlank = name.rindex(' ') # 查找子字符串最后出现的位置
			self.lastName = name[lastBlank+1:]  #分割名字的名与姓
		except:
			self.lastName = name
		self.birthday = None

        
    def getName(self):
		"""返回self的全名"""
		return self.name

    
    def getLastName(self):
		"""返回self的姓"""
		return self.lastName

    
    def setBirthday(self, birthdate):
		"""假设birthday是datetime.date类型
		将self的生日设置为birthday"""
		self.birthday = birthdate

        
    def getAge(self):
		"""返回self的当前年龄,用日表示"""
		if self.birthday == None:
			raise ValueError
		return (datetime.date.today() - self.birthday).days

    
    def __lt__(self, other):  # 相当于less than 简写lt,
        ***使得实例可以比较,返回bull值***
		"""如果self按字母顺序位于other之前,则返回True,否则返回
		   False。首先按照姓进行比较,如果姓相同,就按照全名比较"""
		if self.lastName == other.lastName:
			return self.name < other.name
		return self.lastName < other.lastName

    def __str__(self):
		"""返回self的全名"""
		return self.name
    
只要Person被实例化,就要为__init__函数提供一个实参
sort 是一个多态方法,如果plist是有Person类型元素组成的列表,那么调用plist.sort()会定义在Person类中的__lt__方法来对列表进行排序

pList = [me, him, her]
for p in pList:
	print(p)
pList.sort()
for p in pList:
	print(p)
# 会先输出:
Michael Guttag
Barack Hussein Obama
Madonna
# 再输出:
Michael Guttag
Madonna
Barack Hussein Obama

8.2 继承

  • 不同的类型中有许多通用的属性,如list和str 都有len函数

  • 继承使得程序员能够建立一个类型的层次结构,是可以建立一组彼此相关的抽象

  • 如下例子:MITPerson 是Person的一个子类,所以继承了它的超类的属性,除了继承属性外,子类还可以做如下的事:

    • 添加新的属性,如下增加了类变量nextIdNum、实例变量idNum 和方法getIdNum
    • 覆盖替换超类中的属性,如果调用方法被覆盖则需要区分到底使用超类中的方法版本还是子类中的版本
class MITPerson(Person):
	
    nextIdNum = 0 #identification 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
    
    def isStudent(self):
        return isinstance(self,Student)
    
p1 = MITPerson('Mark Guttag')
p2 = MITPerson('Billy Bob Beaver')
p3 = MITPerson('Billy Bob Beaver')
p4 = Person('Billy Bob Beaver')

print('p1 < p2 =', p1 < p2)
print('p3 < p2 =', p3 < p2)
print('p4 < p1 =', p4 < p1)

# 输出:
p1 < p2 = True
p3 < p2 = False
p4 < p1 = True

p1,p2,p3都是MITPerson类型,使用MITPerson的__lt__方法,
***调用哪种__lt__方法是有表达式的第一个参数决定的***
p4 和 p1比较时按照名字排序
  • nextIdNum是类变量,不是术语MITPerson类的实例,并不创建新实例

8.2.1 多重继承

  • 以下是类层次结构添加了二重继承

  • 结构:

Python 编程导论 Chapter 8 —— 类与面向对象编程_第1张图片

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

class Grad(Student):
	pass

p5 = Grad('Buzz Aldrin')
p6 = UG('Billy Beaver', 1984)
print(p5, 'is a graduate student is', type(p5) == Grad)
print(p5, 'is an undergraduate student is', type(p5) == UG)

#输出:
Buzz Aldrin is a graduate student is True
Buzz Aldrin is an undergraduate student is False

isinstance ()是内置在Python中的,其中第一个参数是任何对象,第二个参数必须是一个type类型的对象
如: isinstance([1, 2], list) 的值是 True

# isStudent()方法在MITPerson中定义
print(p5, 'is a student is', p5.isStudent())
print(p6, 'is a student is', p6.isStudent())
print(p3, 'is a student is', p3.isStudent())
  • 为什么会建立一个没有新属性的类,只是继承了超类的属性?
    • 通过引入Grad 类,创建两种不同类型的学生并使用它们的类型来区分对象

8.2.2 替换原则

  • 使用子类时,应当把子类看做是超类行为的扩展
  • 在子类覆盖超类中的方法时,必须保证超类中的重要行为必须被所有子类支持

8.3 封装与信息隐藏

class Grades(object) :
    def __init__(self) :
        """创建空的成绩表"""
        self.students = []
        self.grades = {}
        self.isSorted = True

    def addStudent(self, student) :
        """假设student为Student类型
            将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) :
        """假设grade为浮点数
        将grade添加到student的成绩列表"""
        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 [:]




def gradeReport(course) :
    """假设course 是Grades 类型"""
    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 report

ug1 = UG('Jane Doe', 2014)
ug2 = UG('John Doe', 2015)
ug3 = UG('David Henry', 2003)
g1 = Grad('Billy Buckner')
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))

# 输出:
Jane Doe's mean grade is 75.0
John Doe's mean grade is 75.0
David Henry has no grades
Billy Buckner's mean grade is 50.0
Bucky F. Dent's mean grade is 87.5
  • 面对对象编程的核心思想

    • 封装,将数据属性和操作数据属性的方法打包在一起
    • 信息隐藏,可以随心所欲秀海类的实现代码,而不用担心会破坏那些使用类的代码
  • 在Python 3中,可以使用命名惯例使属性在类之外不可见。当一个属性的名称以 __ 开头但不以 __ 结束时,这个属性在类外就是不可见的

class infoHiding(object):
    def __init__(self):
        self.visible = 'Look at me'
        self.__alsoVisible__ = 'Look at me too'
        self.__invisible = 'Don\'t look at me directly'
    def printVisible(self):
        print(self.visible)
    def printInvisible(self):
        print(self.__invisible)
    def __printInvisible(self):
        print(self.__invisible)
    def __printInvisible__(self):
        print(self.__invisible)
        
test = infoHiding()
print(test.visible)
print(test.__alsoVisible__)
print(test.__invisible)

# 输出:
Look at me
Look at me too
Error: 'infoHiding' object has no attribute '__invisible'
  • 包含yield语句的函数都会被解释器特殊处理,yield 语句告诉Python系统,这个函数是个生成器,一般生成器与for语句一起使用
# 新版的getStudents
def getStudents(self):
	"""按字母顺序每次返回成绩册中的一个学生"""
		in alphabetical order"""
	if not self.isSorted:
		self.students.sort()
		self.isSorted = True
	for s in self.students:
		yield s

8.4 进阶示例:抵押贷款

  • 编写一个程序,计算以下三种贷款方式的实际成本:
    • 不带“点数”的固定利率贷款
    • 带“点数”的固定利率抵押贷款
    • 初始引诱利率较低,但随后需要支付更高利率的抵押贷款
  • Mortgage 的基类

Python 编程导论 Chapter 8 —— 类与面向对象编程_第2张图片

  • 下图中的类,实现了三种类型的抵押贷款:

Python 编程导论 Chapter 8 —— 类与面向对象编程_第3张图片

  • Fixed 类和FixedWithPts 覆盖了__ init __方法,继承了其余三种方法
  • TwoRate类将抵押贷款看做两种贷款的联结
  • 给定一个参数样本集合后,设立函数计算总成本

Python 编程导论 Chapter 8 —— 类与面向对象编程_第4张图片

  • 输出结果:

    Fixed, 7.0%

    Total payments = 479017

    Fixed, 5.0%, 3.25 points

    Total payments = ​393011

    4.5% for 48 months, then 9.5%

    Total payments = ​$551444

你可能感兴趣的:(MIT,Python,编程导论)