zoukankan      html  css  js  c++  java
  • Python抽象基类之声明协议

    抽象基类之--声明协议

    上回讲了Python中抽象基类的大概,相信大家对abcmeta以及什么是抽象基类已经有所了解。传送门

    现在我们来讲讲抽象基类的另一个常用用法--声明协议

    所谓声明协议,有点像Java中的接口这个概念。就是子类必须实现父类要求的方法。

    1.不使用抽象基类来实现

    1. 提出异常(规范起见请使用 NotImplementedError)

    class Test(object):
        def __init__(self):
            self.salary = []
    
        def work(self):
            money = self._job()
            self.salary.append(money)
            return money
    
        def _job(self):
            raise NotImplementedError("Test子类必须实现 '_job' 方法")
    

    如上所示的类,如果子类没有自己重写_job方法而执行work方法就会产生NotImplementedError异常,这样通过异常的方法就可以强迫子类必须实现父类规定的函数,但是这有个缺点:如果子类不调用work方法,那么子类就算没有实现_job方法也不会报错。

    2. 使用元类

    通过元类也可以实现声明协议的功能

    class TestMeta(type):
        def __new__(cls, name, bases, attrs):
            # 首先判断该类是不是基类(通过手动定义 abstract 属性来判定)
            if attrs.pop("abstract", False):
                return super(TestMeta, cls).__new__(cls, name, bases, attrs)
    
            # 否则就是子类,就要判断子类有没有一个叫 _job 的方法
            _job = attrs.get("_job", None)
    
            if _job and callable(_job):
                return super(TestMeta, cls).__new__(cls, name, bases, attrs)
    
            # 子类为定义 _job 方法
            raise TypeError("Test子类必须要定义 _job 方法")
    
    
    class Test(metaclass=TestMeta):
        abstract = True
    
        def __init__(self):
            self.salary = []
    
        def work(self):
            money = self._job()
            self.salary.append(money)
            return money
    
    
    # 这个类会报错
    # class SubTestA(Test):
    #     pass
    
    class SubTestB(Test):
        def _job(self):
            return 3000
    

    上面的例子中,我们通过元类来检查创建的子类是否包含有_job方法。相比起方法一,该方法必须要求子类定义_job类无论有没有调用过_job方法。但是与Java中的接口相比又少了一点什么?Java中的接口是不可以直接实例化对象的,但是在这个例子中我们可以直接创建Test的对象。这时就该抽象基类上场了。

    2.抽象基类实现声明协议

    我们可以使用abc模块中的一个装饰器--abstractmethod来装饰一个类的方法使其成为一个抽象方法,拥有抽象方法的类就不可以被实例化(像不像C++中的虚函数)举个例子。

    import abc
    
    
    class Test(metaclass=abc.ABCMeta):
        def __init__(self):
            self.salary = []
    
        def work(self):
            money = self._job()
            self.salary.append(money)
            return money
    
        @abc.abstractmethod
        def _job(self):
            raise NotImplementedError("Test子类必须实现 '_job' 方法")
    
    
    # t = Test()    # 报错: TypeError: Can't instantiate abstract class Test with abstract methods _job
    
    
    # 子类可以被创建,但是不能初始化
    class SubTestA(Test):
        pass
    
    
    # sta = SubTestA()    # 报错: TypeError: Can't instantiate abstract class SubTestA with abstract methods _job
    
    # 实现抽象函数的子类可以被实例化
    class SubTestB(Test):
        def _job(self):
            return 3000
    
    stb = SubTestB()
    

    对比一下上述三种方法的区别:

    异常 元类 抽象基类
    在调用方法时产生异常 在定义类时产生异常 在实例化对象时产生异常

  • 相关阅读:
    pytorch_wavelets包实现的小波变换和MWCNN中的小波变换的异同点
    servlet的xx方式传值中文乱码
    类中添加log4j日志
    QueryRunner实战(query_update)、BeanListBeanHandler、MapListMapHandler、ScalarHandler
    java中常用jar包
    QueryRunner类实战
    Json解析包FastJson使用
    BaseDAO使用
    策略模式
    关于动态代理
  • 原文地址:https://www.cnblogs.com/kainhuck/p/11254302.html
Copyright © 2011-2022 走看看