6 继承和抽象类

复用已有的模型构建新的模型,即继承(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循环停止

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值