美文网首页
3-2 抽象基类abc模块

3-2 抽象基类abc模块

作者: xgnb | 来源:发表于2019-03-10 21:42 被阅读0次

    abc ---- Abstract base class
    何为抽象基类:

    • 类比JAVA: 可以当作是JAVA中的接口,在JAVA里面它是无法实现多继承的,JAVA只能继承一个类,但是JAVA可以继承多个接口,而接口是无法实例化的,所以说在python里面它的抽象基类也是不可以实例化的。

    注意:
    我们需要明白一点的就是,python它是动态语言,动态语言他是没有变量的类型的(就是在声明变量是不用 char, int ...什么的),实际上在python中变量它只是一个符号而已,它可以指向任何类型的对象,所以说在python当中,也就不存在有多态的这一个概念。(我们可以赋值任何数据给我们的变量,而且它是可以修改。)
    所以说它就不需要像JAVA那样去实现一个多态,出于语言本身的层面上来讲它原本就是支持多态的一个语言。


    动态语言和静态语言最大的区别在于:

    动态语言不需要要指明变量的类型,所以说动态语言就少了一个编译时检查错误的环境,在python中如果写错了代码实际上是很难知道的,只有在运行的时候才能发现错误,这也是动态语言共有的缺陷。


    python信奉的是鸭子类型

    鸭子类型贯穿python的面向对象当中,我们在使用python或是在设计python类的时候,我们都一定要把鸭子类型放在第一位。

    上节课回顾:
    它和java最大的区别是我们去实现一个class的时候我们是不需要继承指定的类型的。(不要把他们两混为一谈)

    因为本身鸭子类型就是动态语言设计时候的一种非常好的一种理念,鸭子类型和我们的魔法函数实际上构成了我们python语言的基础也就是python里面的一种协议,因为python本身不是去通过继承某一个类或者接口就有某些特性,而是只要去实现某些指定的魔法函数,我们的类就是某种类型的对象。


    那抽象基类是什么个说法?

    答:
    (1)在这个基础的类当中我们去设定好一些方法,然后所有的继承这个基类的类它都必须要覆盖这里面的方法。
    (2)抽象基类是无法用来实例化的

    疑问:既然python是基于鸭子类型去设计的,那为什么又多出抽象基类这个概念呢,我们直接去实现某个方法不就行了嘛?

    我们假设两种用场景:

    (1):我们去检查某个某个类是否有某种方法时

    class Company:
        def __init__(self, employee_list):
            self.employee = employee_list
    
        def __getitem__(self, item):
            return self.employee[item]
    
        def __str__(self):
            return '-'.join(self.employee)
    
        def __len__(self):
            return len(self.employee)
    
    com = Company(['abc', 'cvb'])
    # 一般我们知道有hassattr可以判断。
    #print(hasattr(com,'__len__'))  # --  True
    # 使用抽象接口判断
    from collections.abc import Sized
    print(isinstance(com, Sized))
    

    补充说明:
    根据我们的知识,isinstance是用来判断一个对象的实例,Company都没有继承与Sized,为什么能够正确返回True呢,这就由于python的设计,还是回到鸭子类型这边,基于isinstance函数在python内部的查找优化,以及查找方法(这些我都不会妈个蛋, 是python内部的一些实现方法,不过Sized我可以说明一下)。

    我们从collections.abc中导入了Sized的抽象基类,源码瞄一眼:

    class Sized(metaclass=ABCMeta):
    
        __slots__ = ()
    
        @abstractmethod
        def __len__(self):
            return 0
    
        @classmethod
        def __subclasshook__(cls, C):
            if cls is Sized:
                return _check_methods(C, "__len__")
            return NotImplemented
    

    解释:
    这个Size的抽象基类,有个metaclass=ABCMeta这样的一个标注(别问,这是一种规定的写法他们是抽象基类的写法),我们可以看到他的@abstractmethod 抽象方法(该装饰器,在中abc导入:from abc import abstractmethod), 关键判断它是否有__len__方法在于下面的__subclasshook__魔法函数,(这个函数里面的_check_methods起了关判断作用)

    小结

    综上述也就不难说明为什么isinstance(com, Sized)返回True了。


    我们来看第二个场景:
    我们需要强制某个子类必须实现某些方法(实现一个web框架,继承cache(redis, cache, memorychache),需要设计一个抽象基类,指定子类必须实现某些方法),做一些接口的强制规定

    class Cache(object):
    
        def set(self, key, value):
            raise NotImplementedError  # not Implemented Error ---> 未实现错误
    
        def get(self, key):
            raise NotImplementedError
    
    
    # 用户在重写时需要覆盖带这个方法
    class MyCache(Cache):
        pass
    
    mycache = MyCache()
    mycache.get('ke', 'sv')
    
    
    

    第一种情况我们在基类规定的方法中抛异常,如果子类不重写这个方法直接调用基类的方法则会报错,这是一种解决方案,弊端在于要在调用时才能发现错误,而不是在声明对象实例时。


    让我们来定义一个抽象基类来解决这个问题

    from abc import abstractmethod
    from abc import ABCMeta
    
    class Cache(metaclass=ABCMeta):
        @abstractmethod
        def set(self, key, value):
            pass
        @abstractmethod
        def get(self, key):
            pass
    
    # 用户在重写时需要覆盖带这个方法
    class MyCache(Cache):
        pass
    
    mycache = MyCache()
    

    直接运行看结果:


    直接在我们声明实例对象时就给报错了,需要我们在子类中重写这两个方法才行,我们看重写后的结果。

    from abc import abstractmethod
    from abc import ABCMeta
    
    class Cache(metaclass=ABCMeta):
        @abstractmethod
        def set(self, key, value):
            pass
        @abstractmethod
        def get(self, key):
            pass
    
    # 用户在重写时需要覆盖带这个方法
    class MyCache(Cache):
        def set(self, key, value):
            print('sb')
    
        def get(self, key):
            print('rz')
    
    mycache = MyCache()
    

    结果是能够正确的声明变量。


    总结

    说出来你可能不信,上面的abc抽象基类其实在实际编写的时候是不提倡的,也不提倡使用多继承,我们后面会学习一种设计模式Mixin的设计模式,用于增强丰富对象的功能。

    上面的abc, collection.abc 中提供许多抽象基类,其实它更像是一个文档,带我们去了解python。

    相关文章

      网友评论

          本文标题:3-2 抽象基类abc模块

          本文链接:https://www.haomeiwen.com/subject/vphwcqtx.html