面向对象--接口类、抽象类
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕
(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
依赖倒置原则:
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程
在python中根本就没有一个叫做interface的关键字,上面的代码只是看起来像接口,其实并没有起到接口的作用,子类完全可以不用去实现接口 ,如果非要去模仿接口的概念,可以借助第三方模块: http://pypi.python.org/pypi/zope.interface twisted的twisted\internet\interface.py里使用zope.interface 文档https://zopeinterface.readthedocs.io/en/latest/ 设计模式:https://github.com/faif/python-patterns

接口提取了一群类共同的函数,可以把接口当做一个函数的集合。
然后让子类去实现接口中的函数。
这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由二者分别产生一只老鼠和一只松鼠送到你面前,即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。
再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样
接口类
例子:需求开发多个支付接口(入口)
class Wechat: def pay(self,momey): print('使用微信支付了%s元'%momey) class Ali: def pay(self,momey): print('使用支付宝支付了%s元'%momey) #实例化对象 wechat = Wechat() ali = Ali() wechat.pay(100) ali.pay(200)
发现用户需要调用对象,开始简化用户操作。
class Wechat: def pay(self,momey): print('使用微信支付了%s元'%momey) class Ali: def pay(self,momey): print('使用支付宝支付了%s元'%momey) def pay(softname,money): # 用户统一操作 softname.pay(money) #实例化对象 wechat = Wechat() ali = Ali() # wechat.pay(100) # ali.pay(200) pay(wechat,100) pay(ali,200)
接着,更换了一个程序员,需要新增一个苹果支付;
class Wechat: def pay(self,momey): print('使用微信支付了%s元'%momey) class Ali: def pay(self,momey): print('使用支付宝支付了%s元'%momey) class Applepay: def zhifu(self,money): print('使用苹果支付了%s元'%money) def pay(softname,money): # 用户统一操作 softname.pay(money) #实例化对象 wechat = Wechat() ali = Ali() apple = Applepay() # wechat.pay(100) # ali.pay(200) pay(wechat,100) pay(ali,200) pay(apple,300) # 肯定报错,因为没有定义pay方法,该如何修正这个错误?
主动抛出异常,告知哪里错了。
class Payment(): def pay(self,monery): raise NotImplemented # 没有实现这个方法 class Wechat(Payment): def pay(self,momey): print('使用微信支付了%s元'%momey) class Ali(Payment): def pay(self,momey): print('使用支付宝支付了%s元'%momey) class Applepay(Payment): def zhifu(self,money): print('使用苹果支付了%s元'%money) def pay(softname,money): # 用户统一操作 softname.pay(money) #实例化对象 wechat = Wechat() ali = Ali() apple = Applepay() # wechat.pay(100) # ali.pay(200) pay(wechat,100) pay(ali,200) pay(apple,300) # 肯定报错,因为没有定义pay方法,该如何修正这个错误?
引出接口类或抽象类的规范;
from abc import abstractmethod,ABCMeta class Payment(metaclass=ABCMeta): # 指定元类 是ABCMeta @abstractmethod def pay(self,monery): pass # raise NotImplemented # 没有实现这个方法 class Wechat(Payment): def pay(self,momey): print('使用微信支付了%s元'%momey) class Ali(Payment): def pay(self,momey): print('使用支付宝支付了%s元'%momey) class Applepay(Payment): def zhifu(self,money): print('使用苹果支付了%s元'%money) def pay(softname,money): # 用户统一操作 softname.pay(money) #实例化对象 apple = Applepay() # 只需实例化即可发现错误
规范
规范:接口类或者抽象类都可以
接口类:默认多继承,接口类中的所有的方法都必须不能实现--->从Java演化而来
抽象类:不支持多继承,抽象类中的方法可以实现一些方法 -->从Java演化而来
# 必须导入abc模块里的abstractstaticmethod,ABCMeta,以及引用装饰器 # 且该类里的方法是不能实现的 # 接口类是多继承的类 from abc import abstractstaticmethod,ABCMeta class 类名(metaclass=ABCMeta) @abstractstaticmethod def 方法名(self,参数): pass
接口类的多继承
接口隔离原则:使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口
需求:老虎能走路能游泳,鹅能走能飞能游泳,鸟能飞能走路;使用接口类实现
# tiger 走路 游泳 # swan 走路 游泳 飞 # oldying 走路 飞 from abc import abstractmethod,ABCMeta class Walk_animal(metaclass=ABCMeta): @abstractmethod def walk(self):pass # 这里的方法是不能实现的 class Swim_animal(metaclass=ABCMeta): @abstractmethod def walk(self):pass class Fly_animal(metaclass=ABCMeta): @abstractmethod def walk(self):pass class Tiger(Walk_animal,Swim_animal): def walk(self): pass # 因为接口类是做了规范,无法实现功能,所以需要自己定义一个和接口类同名的方法进行需求的实现,若不同名则报错 def swim(self): pass class Swan(Walk_animal,Swim_animal,Fly_animal): def walk(self): pass def swim(self): pass def fly(self): pass
# 之所以定义接口类,为了规范那些类必须统一,在其他人接手的时候,直接知道那些方法需要使用什么命名 # 接口类 刚好满足接口隔离原则,面向对象开发的思想的规范 # 接口隔离原则:使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口 # 如上例子,不应该定义一个animal的一样总类,应该细分,才方便定义
抽象类
什么是抽象类
与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),而接口类值强调函数属性的相似性。
# 在java的角度上看,是有区别的 # java 本来就支持单继承,所以就有了抽象类; # Java没有多继承,所以为了接口隔离原则,设计了接口这个概念,支持多继承了; # 在Python中 # 即支持单继承也支持多继承,所以对于接口类和抽象类的区别就不那么明显了 # 甚至在Python中并没有内置接口类
抽象类是一个介于类和接口之间的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计。
在Python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。
A.多继承问题
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励使用多继承接口
接口隔离原则:
使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。
B.方法的实现
在抽象类中,我们可以对一些抽象方法做出基础实现;
而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
若面试遇到关于接口类的问题。
注意:1、Python中没有接口类的概念;2、都是一种规范