复用已有的模型构建新的模型,即继承(inheritance)
使用继承定制一个已有的类(利用继承最简单直接的方式)
理想状态下,两个类拥有共同的接口,但其中一个可以实现特殊的行为
如有序包:
1 允许for循环,按照排好的顺序进行访问
2 in运算符以对数时间运行
3 要添加到包中的项,必须能彼此进行比较(识别比较运算符+具有相同的类型)
已有类的子类化
利用继承的特性,使ArraySortedBag类成为ArrayBag的子类
实线箭头表示子类/超类的关系,虚线箭头表示类/接口的关系
在父类文件的一个副本上工作:
1 删除所有不需要修改的方法(通过父类继承自动包含),但是新类中,仍然需要__init__方法
2 为保证可以实现继承,需要将 父类的名称 放在类表明头部的圆括号中
3 修改必须修改的方法+添加新的方法
再谈__init__方法
即便新类中不包括新的实例变量,也必须调用父类中的__init__方法(从而初始化其中的数据)
调用某个父类中方法的语法: <parent class name>.<method name>(self, <other arguments>)
self必须引用ArraySortedBag的一个实例,从而正确运行方法====所以需要将self作为ArrayBag.__init__的一个额外参数传递
因为按照创建常规包的方式创建了一个有序包,所有有序包的__init__方法必须拥有和其父类的__init__方法相同的方法头
'''
ArraySortedBag.py
'''
from arraybag import ArrayBag
class ArraySortedBag(ArrayBag):
def __init__(self, sourceCollection=None):
ArrayBag.__init__(self, sourceCollection)
添加新的方法__contains__
这一方法在有序包项的数组(self._items,在ArrayBag里面所以可以直接用)上实现了一次二叉搜索
def __contains__(self, item):
left = 0
right = len(self)-1
while left <= right:
midPoint = (left+right)//2
if self._items[midPoint] == item:
return True
elif self._items[midPoint] > item:
right = midPoint - 1
else:
left= midPoint + 1
return False
修改已有的add方法
新类中的add需要将一个新项放到有序数组的对应位置上(搜索),想办法传递给ArrayBag的add方法
如果无法传递:查看数组找到第一个大于或等于新项的项,插入新项,包大小+1
'''
arraybag.py
'''
from arrays import Array
from abstractbag import AbstractBag
class ArrayBag(AbstractBag):
DEFAULT_CAPACITY = 10
def __init__(self, sourceCollection=None):
self._items = Array(ArrayBag.DEFAULT_CAPACITY)
AbstractBag.__init__(self, sourceCollection)
def add(self, item):
if self.isEmpty() or item >= self._items[len(self)-1]:
ArrayBag.add(self, item)
else:
targetIndex = 0
while item > self._items[targetIndex]:
targetIndex += 1
for i in range(len(self), targetIndex, -1):
self._items[i] = self._items[i-1]
self._items[targetIndex] = item
self._size += 1
ArrayBag.add<--->self.add
In | __contains__方法 | 最差O(logn) |
__eq__ | 两个同样长度常规包,平均O(n^2) | 平均O(nlogn) |
两个包中要比较的项位于底层数组中的相同的位置
使用抽象类去除代码冗余性
去除一组已有的类的冗余的方法和数据 | 在一个通用的超类(抽象类)中构造代码 |
抽象类捕获了相关的一组类的通用特征和行为,其子类称为具体类:真正用于创建对象的类
最显而易见的冗余方法 | 直接调用其他方法且没有直接访问实例变量的方法 | isEmpty,__str__,__add__,__eq__ |
冗余的实例变量难以识别,包内两个实例变量(self._items, self._size),找出冗余需要查看变量引用的数据类型
Self._items | 数据结构的一个不同的数据类型(不同实现) |
Self._size | 每一个包类中引用整数值(冗余) |
__len__ | 引用了size所以也是冗余的方法 |
将冗余的方法删除,放置到AbstractBag的新类中
注意:AbstractBag类没有实现包接口,因为它仅包含了包方法的一个子集
ArrayBag从祖先类AbatractBag中间接地集成了一些方法和数据
创建AbstractBag类:
1 将其一个子类的内容复制到新文件中,改名
2 删除任何不相关的导入,类改名
3 删除访问实例变量self._items的所有方法,除了__init__方法
'''
abstractbag.py
'''
class AbstractBag(object):
def __init__(self, sourceCollection=None):
self._size = 0
if sourceCollection:
for item in sourceCollection:
self.add(item)
修改AbstractBag的子类:所有子类都需要导入这个类,将其名称放置在类头部的圆括号中,省略冗余方法并包含一个修改后的__init__方法
修改ArrayBag(上上述代码)
init中两条语句的顺序很关键
在运行超类的构造方法之前,将self._items初始化为新的数组,以便有地方存储添加到新包中的任何项
将AbstractBag中的__add__方法泛化
不仅仅针对特定类创建实例,而是self类型的实例
type函数看self类型,从而创建self的一个副本
以下是适用所有包类型的一个add方法:
所有集合的一个抽象类
AcstractBag中所有方法都运行其他方法和函数,不会提及包类,其方法也是可以在其他任何集合类型上运行的方法====>应当将这些整合到一个更加通用的抽象类中====>即便其他还没有开发的集合类型也能使用他们
将AbstractCollection整合到集合层级中
AbstractCollection负责引入和初始化self._size变量
创建:
1 复制AbstractBag中代码,改名
2 删除父类对__init__方法的调用,修改
3 修改__str__和__eq__方法,提供合理的默认行为
删除isEmpty, len , add方法
在__eq__方法中使用两个迭代器
只能在一个集合上运行一个for循环的话,需要操作第二个集合的迭代器对象(iter函数)
在迭代器对象上调用next函数会返回该集合的迭代器序列中的当前项,并且前进到下一个项,若无当前项,next函数返回 StopIteration异常
在AbstractCollection中eq方法不需要捕获stopIteration异常:两个集合相同长度,第二个集合迭代器到末尾时,第一个集合的for循环停止