【Python单点知识】通过实例介绍抽象类

文章目录

  • 0. 前言
  • 1. 抽象类的概念与特性
  • 1.1 定义
  • 1.2 特性
  • 2. 抽象类的实现与使用
  • 2.1 抽象类的创建
  • 2.2 抽象类的特性验证
  • 2.3 注册机制与非直接继承
  • 3. 应用场景与设计价值
  • 4. 总结
  • 0. 前言

    按照国际惯例,首先声明:本文只是我自己学习的理解,虽然参考了他人的宝贵见解及成果,但是内容可能存在不准确的地方。如果发现文中错误,希望批评指正,共同进步。

    本文介绍Python中的抽象类。

    封装了共享的属性和方法,是对象的抽象。而抽象类是对的抽象,即代表了一类的,是定义接口规范和强制子类实现特定方法的特殊类,旨在实现多态性、代码重用与模块化设计。

    有点无法直视类这个字了……

    为了让这个说明不这么绕,这里需要举一个例子:假如我们有两个对象老师学生,正常我们需要分别面向这两个对象进行编程,完整地构建这两个类所包含的所有方法和属性。

    然后我们通过总结归纳,发现这两个类其实可以从一个更通用的基类——衍生出来,这里的即为抽象类,而老师学生即为子类,基类规定了子类的接口规范。

    这个抽象类既规定了其子类必须强制实现的方法,比如显示名字、年龄等,这些都是作为必须拥有的属性;也规定了可以选择实现的方法,比如工资,老师才有工资,而学生没有。

    上面的例子先不考虑类的继承。

    1. 抽象类的概念与特性

    1.1 定义

    抽象类是一种不能被直接实例化的类,它主要用于定义一组抽象方法,并作为其他类(子类)的基类。Python中实现抽象类的功能主要依赖于内置的 abc(Abstract Base Classes)模块。

    1.2 特性
  • 接口规范:抽象类通过声明抽象方法,为子类定义了一个公共接口。这个接口规定了子类必须提供的方法名称、参数列表和返回类型,形成了类与类间交互的契约。

  • 强制实现:子类在继承抽象类时,必须实现所有抽象方法。否则,试图实例化子类会引发 TypeError,确保子类具备抽象类所要求的基本功能。

  • 多态性:抽象类定义的接口允许不同子类根据各自业务需求提供不同的实现。通过抽象基类的引用,程序可以以统一的方式处理多种子类对象,实现了“一种接口,多种行为”的多态特性。

  • 非抽象成员:抽象类不仅可以包含抽象方法,还可以定义普通方法(有实现)和属性。这些非抽象成员可以提供通用逻辑或默认行为,子类可以选择是否重写或补充。

  • 类型检查与注册:除了直接继承外,Python的 abc 模块允许通过注册机制声明一个类实现了某个抽象基类的接口,从而进行类型检查和验证,即使该类并未直接继承抽象基类。

  • 2. 抽象类的实现与使用

    2.1 抽象类的创建

    在Python中创建抽象类,首先需要导入 abc 模块,并继承 abc.ABC 类(或使用 abc.ABCMeta 元类)。接着,使用 @abc.abstractmethod 装饰器标识抽象方法。

    首先我们来看一个简单的示例:

    import abc
    
    class human(abc.ABC):
        @abc.abstractmethod
        def show_basic_info(self, arg):
            """声明一个抽象方法,子类必须实现"""
            pass
    
        def calculate_salary(self, arg):
            """一个普通方法,提供默认行为,子类可选重写"""
            print("Default behavior in the abstract base class")
    
    class teacher(human):
        def show_basic_info(self, name, age, if_married = 'Is'):
            print("name:%s"%name)
            print("age:%i"%age)
            print("%s married"%if_married)
    
        def calculate_salary(self, teach_year):
            if teach_year > 20:
                print('salary is $2000')
    
            else:
                print('salary is $1000')
    
    class student(human):
        def show_basic_info(self, name, age, grade):
            print("name:%s"%name)
            print("age:%i"%age)
            print("grade:%i"%grade)
    
    MrLee = teacher()
    MrLee.show_basic_info('Lee', 28, 'Is not')
    MrLee.calculate_salary(5)
    print('\n')
    Tommy = student()
    Tommy.show_basic_info('Tommy',12,6)
    

    输出:

    name:Lee
    age:28
    Is not married
    salary is $1000
    
    
    name:Tommy
    age:12
    grade:6
    
    2.2 抽象类的特性验证

    现在我们再回过头来验证下上面说的抽象类的特性:

    1. 接口规范:这点通过上面的示例可以明显看出,无需多说。

    2. 强制实现:需要强制实现的方法会用装饰器@abc.abstractmethod标注。对于上段代码,如果我们把calculate_salary()方法也标注一下:

    import abc
    class human(abc.ABC):
        @abc.abstractmethod
        def show_basic_info(self, arg):
            """声明一个抽象方法,子类必须实现"""
            pass
    
        @abc.abstractmethod   #变成强制实现的方法
        def calculate_salary(self, arg):
            """一个普通方法,提供默认行为,子类可选重写"""
            print("Default behavior in the abstract base class")
    class teacher(human):
        def show_basic_info(self, name, age, if_married = 'Is'):
            print("name:%s"%name)
            print("age:%i"%age)
            print("%s married"%if_married)
    
        def calculate_salary(self, teach_year):
            if teach_year > 20:
                print('salary is $2000')
    
            else:
                print('salary is $1000')
    
    class student(human):
        def show_basic_info(self, name, age, grade):
            print("name:%s"%name)
            print("age:%i"%age)
            print("grade:%i"%grade)
    
    MrLee = teacher()
    MrLee.show_basic_info('Lee', 28, 'Is not')
    MrLee.calculate_salary(5)
    print('\n')
    Tommy = student()
    Tommy.show_basic_info('Tommy',12,6)
    

    student类实例化过程就会报错,而teacher类实现了这个方法,就不会报错:

    TypeError: Can't instantiate abstract class student with abstract method calculate_salary
    name:Lee
    age:28
    Is not married
    salary is $1000
    
    1. 多态性:上面这个示例中,虽然teacherstudent子类都有show_basic_info()方法,但是它们根据不同子类的不同需求,有着不同的定义,即实现了“一种接口,多种行为”。
    2. 非抽象成员:例如可以增加一个announce()方法:
    import abc
    class human(abc.ABC):
        @abc.abstractmethod
        def show_basic_info(self, arg):
            """声明一个抽象方法,子类必须实现"""
            pass
    
        def calculate_salary(self, arg):
            """一个普通方法,提供默认行为,子类可选重写"""
            print("Default behavior in the abstract base class")
    
        def announce(self):  #增加一个具体的方法
            print("I'm a human")
    
    class student(human):
        def show_basic_info(self, name, age, grade):
            print("name:%s"%name)
            print("age:%i"%age)
            print("grade:%i"%grade)
            
    Tommy = student()
    Tommy.show_basic_info('Tommy',12,6)
    Tommy.announce()
    

    输出为:

    name:Tommy
    age:12
    grade:6
    I'm a human  #新增加的非抽象成员
    
    2.3 注册机制与非直接继承

    对于不直接继承抽象基类但仍希望符合其接口规范的类,可以使用 abc.ABCMeta.register() 方法进行注册,我们再改造下上文的例子:

    import abc
    class human(abc.ABC):
        @abc.abstractmethod
        def show_basic_info(self, arg):
            """声明一个抽象方法,子类必须实现"""
            pass
    
        def calculate_salary(self, arg):
            """一个普通方法,提供默认行为,子类可选重写"""
            print("Default behavior in the abstract base class")
    
        def announce(self):  #增加一个具体的方法
            print("I'm a human")
    
    class worker(abc.ABC):
        pass
    
    class firefighter(worker):
        def show_basic_info(self, age):
            print("age:%i"%age)
    
    John = firefighter()
    John.show_basic_info(23)
    print(isinstance(John, worker))  #True
    print(isinstance(John, human))   #False
    
    human.register(firefighter)
    print(isinstance(John, worker))  #True
    print(isinstance(John, human))   #True
    

    这里的John是子类firefigher的实例,firefight原本仅是worker的子类,但是通过观察发现firefight也符合human的接口,于是可以通过.register()firefight注册为human的子类。

    3. 应用场景与设计价值

    1. 设计原则与设计模式

    抽象类是实现“依赖倒置原则”、“开闭原则”等设计原则的关键工具,同时也是“策略模式”、“模板方法模式”等设计模式的基础构造块。它们有助于构建松耦合、高内聚的系统,使其易于应对变化和扩展。

    2. 大型项目与框架开发

    在复杂的软件项目和框架开发中,抽象类常被用来定义核心组件的接口标准。例如,Web框架可能定义一个抽象视图类,规定子类必须实现 render() 方法来呈现网页内容。这种标准化接口确保了框架与用户自定义代码之间的协调一致。

    3. API设计

    对外提供API时,抽象类可以作为客户端实现特定接口的指南。通过定义抽象服务类或接口类,API文档可以直接引用这些类来说明预期的行为和方法签名,简化了第三方集成过程。

    4. 总结

    Python抽象类作为设计和组织复杂代码的强大工具,通过定义接口规范、强制子类实现特定方法以及支持多态性,极大地增强了代码的可读性、可扩展性和可维护性。无论是大型项目、框架开发,还是API设计,正确理解和运用抽象类都能带来显著的架构优势。通过结合 abc 模块提供的功能,开发者可以构建更为健壮、灵活的面向对象系统,满足不断变化的需求和未来的扩展。

    作者:使者大牙

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【Python单点知识】通过实例介绍抽象类

    发表回复