欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

python基础第十九课--OOP定义一个接口或抽象基类(小白piao分享)

程序员文章站 2022-07-14 18:59:46
...

抽象基类:

    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
相关标签: python基础