首先要说明的是,python中没有接口类但有抽象类,但由于很多程序员是从java转python的,所以java的面向对象的思想和概念也或多或少地影响了程序员的编程思维和用法习惯:以下是java和python的对比:
java :
1, java里的所有类的继承都是单继承,所以抽象类就能完美的解决单继承需求中的规范问题
2, 但对于多继承的需求,由于java本身语法不支持类的多继承,所以创建了接口Interface这个概念来解决多继承的规范问题 (但可以继承多个接口Interface)
python:
1,python中没有接口类 :python中自带多继承 所以我们直接用class来实现了接口类
2,python中支持抽象类 : 一般情况下 单继承 不能实例化 且可以实现部分python代码
也即是说,我们在python中使用abc抽象类模块既可以实现抽象类,也可以利用abc抽象类模块实现接口类,但我们要在用法上java看齐,在当成一个抽象类时(提取多个类的共性),最好是单继承的,在当成一个接口类时,推荐使用多继承。
不管是抽象类还是接口类 : 它们都是面向对象的开发规范 所有的接口类和抽象类都不能实例化。
注意
1.多继承问题
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励你来多继承接口
2.方法的实现
在抽象类中,我们可以对一些抽象方法做出基础实现(因为提取的是所有子类的共性);
而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)
且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
三、接口隔离原则:使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口
接口类:基于同一个接口实现的类 刚好满足接口隔离原则 面向对象开发的思想 规范
接口类,python 原生不支持 在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念
“”"
我们来看一段代码去了解为什么需要接口类
class Alipay:
def pay(self,money):
print('支付宝支付了')
class Applepay:
def pay(self,money):
print('苹果支付了')
class Weichet:
def pay(self,money):
print('微信支付了')
def pay(payment,money): # 支付函数,总体负责支付,对应支付的对象和要支付的金额
payment.pay(money)
ali=Alipay()
pay(ali,200) #支付宝支付了
调用者通过调用一个统一的支付函数pay,并将支付方式对象传入函数中就能实现不同种类的支付方式。(这十分像len()
内置函数,它在内部也只是调用了传入对象的__len__()
而已,各种对象实现__len__()
的方法由各自对象决定,但len()
实现了调用的统一)
但是开发中容易出现一些问题,那就是类中的函数名不一致,就会导致调用的时候找不到类中对应方法,例题如下:
class Alipay:
def paying(self,money): #这里类的方法可能由于程序员的疏忽,写的不是一致的pay,导致后面调用的时候找不到pay
print('支付宝支付了')
class Applepay:
def pay(self,money):
print('苹果支付了')
class Weichet:
def pay(self,money):
print('微信支付了')
def pay(payment,money): # 支付函数,总体负责支付,对应支付的对象和要支付的金额
payment.pay(money)
ali=Alipay() #这里不报错
pay(ali,200) #调用执行时就会报错,'Alipay' object has no attribute 'pay'
这时候怎么办呢?可以定义一个抽象类Payment,里面定义一个pay方法但不实现它,在里面采用手动抛异常raise NotImplementedError
的方法来提醒开发者继承于Payment类的子类需要实现pay方法:
class Payment:
def pay(self):
raise NotImplementedError #手动抛异常
class Alipay(Payment):
def paying(self, money): # 这里类的方法不是一致的pay,导致后面调用的时候找不到pay
print('支付宝支付了')
class Applepay(Payment):
def pay(self,money):
print('苹果支付了')
class Weichet(Payment):
def pay(self,money):
print('微信支付了')
def pay(payment, money): # 支付函数,总体负责支付,对应支付的对象和要支付的金额
payment.pay(money)
ali = Alipay() # 不报错
pay(ali , 200) # 调用的时候才会报错 'NotImplementedError'
一般这种写法就够用了,但这种方法只有在调用支付对象的pay方法时才会报错,在实例化支付对象时是不会报错的。
所以python也提供了abc抽象类模块,它使得我们在实例化一个子类对象时就能检查出子类有没有实现接口类中规定的方法:
from abc import abstractmethod, ABCMeta #接口类中定义了一些接口名:Pay,且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
class Payment(metaclass=ABCMeta): #抽象出的共同功能Pay
@abstractmethod
def pay(self,money):
pass #这里面的pay 来源于下面类中的方法pay,意思把这个方法规范为统一的标准,另外建一个规范类Payment
class Alipay(Payment):
def paying(self, money): #这里出现paying和我们规范的pay不一样,那么在实例化 Alipay的时候就会报错
print('支付宝支付了')
class Applepay(Payment):
def pay(self,money):
print('苹果支付了')
class Weichet(Payment):
def pay(self,money):
print('微信支付了')
def pay(pay_obj,money):
pay_obj.pay(money)
ali=Alipay() #实例化的时候就会报错 Can't instantiate abstract class Alipay with abstract methods pay 之前两个例子都是在执行的时候报错,这里不一样的是实例化就会知道是哪里发生错误了
总结:用abc模块装饰后,在实例化的时候就会报错,那么当我们代码很长的时候,就可以早一点预知错误,所以以后在接口类类似问题中用这个模块
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,
可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
from abc import abstractmethod,ABCMeta
class Walk_animal(meteaclass=ABCMeta):
@abstractmethod
def walk(self):
print('walk')
class Swim_animal(meteaclass=ABCMeta):
@abstractmethod
def swim(self):
pass
class Fly_animal(metaclass=ABCMeta)
@abstractmethod
def fly(self):
pass
# 通常一个老虎有跑和跑的方法的话,我们会这么做,但是大量的动物都这么写代码就显得冗余
class Tiger:
def walk(self):
pass
def swim(self):
pass
# 但是我们使用接口类多继承的话就简单多了,并且规范了相同功能
class Tiger(Walk_animal,Swim_animal):
pass
#如果此时再有一个天鹅swan,会飞,走,游泳 那么我们这么做
class Swan(Walk_animal,Swim_animal, Fly_animal):
pass
# 怎么样,是不是清晰明了,这就是接口多继承
# 接口类一般要符合单一接口原则,这样继承的子类只继承自己需要的,不需要的一个也不继承。
抽象类: 抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性
1.抽象类是一个介于类和接口之间的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
2.在继承抽象类的过程中,我们应该尽量避免多继承;
3.而在继承接口的时候,我们反而鼓励你来多继承接口;
一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现
多继承的情况 由于功能比较复杂,所以不容易抽象出相同的功能的具体实现写在父类中
为什么要有抽象类:
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的
举例:一切皆文件:
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)