python基础第十九课--OOP定义一个接口或抽象基类(小白piao分享)
抽象基类:
1、核心特征:
抽象基类的核心是不能直接进行实例化,如果尝试对抽象基类进行实例化,将会得到如下的结果:
from abc import ABCMeta,abstractmethod
class Istream(metaclass=ABCMeta):
@abstractmethod
def read(self,maxbytes = -1):
pass
@abstractmethod
def write(self):
pass
io = Istream()
#TypeError: Can't instantiate abstract class Istream with abstract methods read, write
2、功能:
可以在此之上进行类型检查,并确保在子类中实现特定的办法。用来给其他类当做基类使用,这些子类需要实现基类中要求的那些方法。换句话说,就是强制规定所需的编程接口。
如果子类并未实现抽象基类中提及的所有方法,则会出现如下问题:
from abc import ABCMeta,abstractmethod
class Istream(metaclass=ABCMeta):
@abstractmethod
def read(self,maxbytes = -1):
pass
@abstractmethod
def write(self):
pass
#io = Istream()#TypeError: Can't instantiate abstract class Istream with abstract methods read, write
class IoMethod(Istream):
def read(self,maxbytes = -1):
print('1')
io = IoMethod()
#TypeError: Can't instantiate abstract class IoMethod with abstract methods write
#报错说白了就是说基类中的write方法没有被重写!如果连read都不写只写一个pass,那就以此类推会报错:
'''
class IoMethod(Istream):
pass
'''
#TypeError: Can't instantiate abstract class IoMethod with abstract methods read,write
产生上述问题的原因也很简单,子类会继承父类的方法,但是由于父类中的方法为抽象方法在子类中未重写,所以这也是可以理解的。
上述还有一个问题:如果抽象类中假如说小白将一个重整方法__write()添加在其中会怎样?如下:
from abc import ABCMeta,abstractmethod
class Istream(metaclass=ABCMeta):
@abstractmethod
def read(self,maxbytes = -1):
pass
@abstractmethod
def __write(self):
pass
#io = Istream()#TypeError: Can't instantiate abstract class Istream with abstract methods read, write
class IoMethod(Istream):
def read(self,maxbytes = -1):
print('1')
def __write(self):#注意这里!!!这是一个基类中的重整方法(私有方法)
print('2')
io = IoMethod()
#请仔细看下报错原因:
#TypeError: Can't instantiate abstract class IoMethod with abstract methods _Istream__write
所以,这里怎么办呢?有同学会说,为这个私有方法再设计一个get方法:
from abc import ABCMeta,abstractmethod
class Istream(metaclass=ABCMeta):
@abstractmethod
def read(self,maxbytes = -1):
pass
@abstractmethod
def __write(self):
pass
@abstractmethod
def get_write(self):
self.__write()
这样就可以吗?答案是不行:因为抽象基类的所有方法都需要在子类中实现,没办法逃避只设计get不管__write(),换句话说,两种方法都得设计,另外:你在抽象类中如上写get方法,在子类中还是得重写这个方法,所以基类中的self.__write没有任何作用。那如下这样是否可以:
from abc import ABCMeta,abstractmethod
class Istream(metaclass=ABCMeta):
@abstractmethod
def read(self,maxbytes = -1):
pass
@abstractmethod
def __write(self):
pass
#注意这行没有abs装饰器了
def get_write(self):
self.__write()
这样想问题同样片面了,__write()还是没有定义啊。那怎么办呢?很多同学到这里就想着放一放再说了,可是,一鼓作气打下来才是学习的必经之路。
不妨试试这样是否可以:
from abc import ABCMeta,abstractmethod
class Istream(metaclass=ABCMeta):
@abstractmethod
def read(self,maxbytes = -1):
pass
@abstractmethod
def __write(self):
pass
class IoMethod(Istream):
def read(self,maxbytes = -1):
print('1')
def _Istream__write(self):#这样做并不安全,是代码健壮性非常差,非常脆弱的代码结构。
print('hello')
io = IoMethod()
io._Istream__write()# 打印了hello,并且没有了异常
有些好问的同学可能又会问道,那怎么证明这个_Istream__write()就是抽象基类的方法呢?万一是自己类中定义的呢?这个问题显而易见,如果是自己类中重新定义的,那么父类(抽象基类)中的__write()你重写了么?如果没有重写?为什么不会报异常出来呢?咱们很早就说过了,抽象类中的所有方法都是需要在子类中重写的。
但是上过课的同学(有需要的同学,请联系小白piao。)知道,这个东西这样子做,真的好吗?真的安全吗?答案当然是不安全的,所以当我们在设计应用层而非底层的类时,尽量不要在抽象类中出现私有(名称重整)方法
上文提到,抽象基类是为了强制规定所需的接口的。有些同学会问:那么在其子类中是否允许定义基类中没有的方法呢?
from abc import ABCMeta,abstractmethod
class Istream(metaclass=ABCMeta):
@abstractmethod
def read(self,maxbytes = -1):
pass
@abstractmethod
def write(self):
pass
class IoMethod(Istream):
def read(self,maxbytes = -1):
print('1')
def write(self):
print('2')
def serialsize(self):
pass
io = IoMethod()
上边不会报错,证明可以再子类中扩充其他方法,所以,这个概念的意思是:必须要定义抽象基类中的所有方法,但是可以扩展其他方法。换个角度,这个问题其实有点幼稚,仔细想想,如果子类只能写抽象基类中有的方法,那么为何还需要定义抽象基类呢?子类只是定义了父类的方法。这里注意,重点是为了为子类限定一些必须定义的接口。
3、讨论:
抽象基类允许其他类向其注册,然后实现所需要的接口:
class Istream(metaclass=ABCMeta):
@abstractmethod
def read(self,maxbytes = -1):
pass
@abstractmethod
def write(self):
pass
class PrtMethod:
def prtInfo(self):
print(self.__dict__)
Istream.register(PrtMethod)
pt = PrtMethod()
print(isinstance(pt,Istream))#True