[ python ] 接口类和抽象类
接口类
继承有两种用途:
1. 继承基类的方法,并且做出自己的改变或者扩展(代码重用)
2. 申明某个子类兼容于某基类,定义一个接口类interface,接口类定义了一些接口名且未实现接口的功能,子类继承接口类,并且实现接口中的功能
class Wechat: ''' 微信支付 ''' def pay(self, money): print('已经用微信支付%s元.' % money) class Alipay: ''' 支付宝支付 ''' def pay(self, money): print('已经用支付宝支付%s元.' % money) def pay(pay_obj, money): ''' 支付函数,总体负责支付 对应支付的对象和要支付的金额 ''' pay_obj.pay(money) ali = Alipay() pay(ali, 200)
如果我们又定义了一个支付类且没有定义pay方法:
class Wechat: ''' 微信支付 ''' def pay(self, money): print('已经用微信支付%s元.' % money) class Alipay: ''' 支付宝支付 ''' def pay(self, money): print('已经用支付宝支付%s元.' % money) class Applepay: def fuqian(self, money): print('已经用Applepay支付%s元.' % money) def pay(pay_obj, money): ''' 支付函数,总体负责支付 对应支付的对象和要支付的金额 ''' pay_obj.pay(money) # 因为 Applepay 类中未定义 pay 方法 ali = Alipay() pay(ali, 200) apple = Applepay() pay(apple, 300) # 报错
通过上面的例子,报错是因为 Applepay 类中没有定义 pay 方法。在这里我们可以通过继承的关系来手动抛出异常。
class Payment: # 创建一个基类 def pay(self, money): # 基类中包含 pay 方法 raise NotImplemented # 主动抛出异常 class Wechat(Payment): ''' 微信支付 ''' def pay(self, money): print('已经用微信支付%s元.' % money) class Alipay(Payment): ''' 支付宝支付 ''' def pay(self, money): print('已经用支付宝支付%s元.' % money) class Applepay(Payment): def fuqian(self, money): print('已经用Applepay支付%s元.' % money) def pay(pay_obj, money): ''' 支付函数,总体负责支付 对应支付的对象和要支付的金额 ''' pay_obj.pay(money) ali = Alipay() pay(ali, 200) apple = Applepay() pay(apple, 300) # 报错
已知 Applepay 类中没有 pay 方法,则会去基类中寻找,基类中定义的 pay 方法是主动抛出异常。
可以使用 abc 模块来实现接口,自动的去检查是否实现了某方法
from abc import ABC, abstractclassmethod, ABCMeta class Payment(metaclass=ABCMeta): @abstractclassmethod def pay(self, money): raise NotImplemented class Wechat(Payment): ''' 微信支付 ''' def pay(self, money): print('已经用微信支付%s元.' % money) class Alipay(Payment): ''' 支付宝支付 ''' def pay(self, money): print('已经用支付宝支付%s元.' % money) class Applepay(Payment): def fuqian(self, money): print('已经用Applepay支付%s元.' % money) def pay(pay_obj, money): ''' 支付函数,总体负责支付 对应支付的对象和要支付的金额 ''' pay_obj.pay(money) ali = Alipay() pay(ali, 200) apple = Applepay() # 实例化就能检查出子类是否创建了 pay 方法
实践中,单纯的从子类继承父类的意义不大,甚至有害,因为它使得子类与基类出现强耦合。
继承的第二种含义就非常重要,它又叫‘接口继承’
接口继承实质上是要求‘做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理,实现了特定接口的所有对象’ -- 这在程序设计上,叫做归一化。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合。
抽象类
什么是抽象类:
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
为什么要有抽象类:
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,
要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。
这一点与接口有点类似,但其实是不同的,即将揭晓答案
#一切皆文件 import abc #利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定义抽象方法,无需实现功能 def read(self): '子类必须定义读功能' pass @abc.abstractmethod #定义抽象方法,无需实现功能 def write(self): '子类必须定义写功能' pass # class Txt(All_file): # pass # # t1=Txt() #报错,子类没有定义抽象方法 class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #这样大家都是被归一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
抽象类与接口类
抽象类的本质还是类,指的是一组类的相似性包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。