自学内容网 自学内容网

python: 抽象基类详解

思维导入

原始表达式:
( A ⋅ B 1 ⋅ C ) (A \cdot B1 \cdot C) (AB1C)
( A ⋅ B 2 ⋅ C ) (A \cdot B2 \cdot C) (AB2C)
( A ⋅ B 3 ⋅ C ) (A \cdot B3 \cdot C) (AB3C)

这个表达式表示三个矩阵乘法的序列,其中 A、B1、B2、B3 和 C 都是矩阵,且 A 和 C 与不同的 B 矩阵相乘。

分解后的表达式:
A ⋅ C ⋅ ( B 1 B 2 B 3 ) A \cdot C \cdot \begin{pmatrix} B1 \\ B2 \\ B3 \end{pmatrix} AC B1B2B3

这里,A 和 C 首先相乘,然后将结果与一个垂直堆叠的矩阵相乘,这个垂直堆叠的矩阵由 B1、B2 和 B3 组成。这种分解可以简化计算,特别是当 A 和 C 的乘积可以被重复使用时。

通过预先计算 A*C 的结果,可以避免在每次与 不同的 B 矩阵相乘时重复计算 A 和 C 的乘积。这类似于在抽象基类中定义通用方法,以避免在多个子类中重复相同的代码,而不同的B矩阵就类似于各个子类中的重写的抽象方法;

在这里插入图片描述

在这里插入图片描述


什么是抽象基类?

在 Python 中,抽象基类(Abstract Base Class,简称 ABC)是一种特殊的类,用于定义其他类的接口,旨在提供一种强制性机制,确保子类实现预定义的方法。抽象基类不能被实例化,通过继承和扩展它们,开发者可以设计一致性强、结构明确的代码。

Python 提供了 abc 模块来支持抽象基类的创建和使用。
当我们有这样的需求的时候,抽象基类就是一个很好的选择:子类必须实现某些指定的方法和属性,否则就抛异常。


抽象基类的主要特点:

  1. 不能被实例化:抽象基类不能直接创建对象,尝试实例化会抛出 TypeError
  2. 包含抽象方法:抽象方法是没有实现体的方法,子类必须实现这些方法,否则它们也会变成抽象类,不能被实例化。
  3. 强制子类实现接口:通过定义抽象方法,你可以确保所有子类都实现了这些方法。

如何创建抽象基类:

  1. 导入 abc 模块

    from abc import ABC, abstractmethod
    
  2. 定义抽象基类
    使用 ABC 类作为基类,并使用 @abstractmethod 装饰器标记抽象方法。

    class MyAbstractClass(ABC):
        @abstractmethod
        def my_abstract_method(self):
            pass
    

    在这个例子中,MyAbstractClass 是一个抽象基类,my_abstract_method 是一个抽象方法。

示例:

  • 假设需要读取两个不同 Excel 文件的内容,但每个文件的读取逻辑不同。此时,使用抽象基类可以实现通用操作的封装,并强制子类实现文件读取的具体逻辑。

使用抽象基类

class GetExcelFileDatas(ABC):

    def __init__(self, file_path): 
        self.file_path = file_path  # 用于存储excel文件路径
        self.workbook = None  # 用于存储加载的工作簿对象
        self.sheet = None  # 用于存储选择的工作表对象

    def __enter__(self):
        """
        第一步:加载 excel 文件
        """
        try:

            self.workbook = openpyxl.load_workbook(self.file_path, keep_vba=True)
            logger.info(f"成功加载工作簿:{self.file_path}")
        except FileNotFoundError:
            logger.error(f"文件 '{self.file_path}' 未找到。")
        except Exception as e:
            logger.error(f"加载工作簿时发生错误: {e} {traceback.format_exc()}")
        return self

    def load_sheet(self, sheet_name=None):
        """
        第二步:选择并加载指定工作表。

        参数:
            sheet_name (str): 需要读取的工作表名称。
        """
        if self.workbook:
            if sheet_name is not None and sheet_name in self.workbook.sheetnames:
                self.sheet = self.workbook[sheet_name]
                logger.info(f"成功加载工作表:'{sheet_name}'")
            elif sheet_name is None:
                self.sheet = self.workbook.active
                logger.info(f"默认加载第一个工作表")
            else:
                logger.info(f"工作簿中不存在工作表:'{sheet_name}',默认加载第一个工作表")
                self.sheet = self.workbook.active
        else:
            logger.info("请先加载工作簿。")

    @abstractmethod
    def read_data(self, **kwargs):
        """
        抽象方法,子类必须实现的方法
        第三步:读取工作表数据
        """

        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        最后一步:关闭工作簿,确保资源释放。
        """
        if self.workbook:
            try:
                self.workbook.close()
                logger.info(f"成功关闭工作簿:{os.path.basename(self.file_path)}")
            except Exception as e:
                logger.error(
                    f"关闭boms工作簿:{os.path.basename(self.file_path)} 时发生错误: {e} {traceback.format_exc()}")


class GetExcelFileOneDatas(GetExcelFileDatas):
# 当子类没有初始化方法的时候,会基础父类的初始化方法;
# 当子类需要自己的初始化方法的时候,在初始化方法中使用super().__init__(父类参数)来继承父类的初始化方法;
    def read_data(self, **kwargs):
        """
        第三步:读取工作表数据
        """
        # 读取出sheet里面的信息,构建成自己想要的数据结构,然后返回结果

        data = "测试,获取到第一个excel中指定sheet的数据"
        return data


class GetExcelFileTwoDatas(GetExcelFileDatas):

    def read_data(self, **kwargs):
        """
        第三步:读取工作表数据
        """
        # 读取出sheet里面的信息,构建成自己想要的数据结构,然后返回结果

        data = "测试,获取到第二个excel中指定sheet的数据"
        return data


def get_all_excel_data(file_path_one, file_path_two):
    # 读取第一个excel文件

    with GetExcelFileOneDatas(file_path_one) as excel_one:
        excel_one.load_sheet(sheet_name="Sheet1")
        data_one = excel_one.read_data()

    # 读取第二个excel文件
    with GetExcelFileTwoDatas(file_path_two) as excel_two:
        excel_two.load_sheet(sheet_name="Sheet2")
        data_two = excel_two.read_data()

    # 返回两个excel文件的数据
    return data_one, data_two


get_all_excel_data("file_path_one", "file_path_two")

未使用抽象基类

# 获取第一个excel文件的内容 
class GetExcelFileOneDatas:

    def __init__(self, file_path):
        self.file_path = file_path  # 用于存储excel文件路径
        self.workbook = None  # 用于存储加载的工作簿对象
        self.sheet = None  # 用于存储选择的工作表对象

    def load_workbook(self):
        """
        第一步:加载 excel 文件
        """
        try:
            self.workbook = openpyxl.load_workbook(self.file_path, keep_vba=True)
            logger.info(f"成功加载工作簿:{self.file_path}")
        except FileNotFoundError:
            logger.error(f"文件 '{self.file_path}' 未找到。")
        except Exception as e:
            logger.error(f"加载工作簿时发生错误: {e} {traceback.format_exc()}")

    def load_sheet(self, sheet_name=None):
        """
        第二步:选择并加载指定工作表。

        参数:
            sheet_name (str): 需要读取的工作表名称。
        """
        if self.workbook:
            if sheet_name is not None and sheet_name in self.workbook.sheetnames:
                self.sheet = self.workbook[sheet_name]
                logger.info(f"成功加载工作表:'{sheet_name}'")
            elif sheet_name is None:
                self.sheet = self.workbook.active
                logger.info(f"默认加载第一个工作表")
            else:
                logger.info(f"工作簿中不存在指定工作表:'{sheet_name}',默认加载第一个工作表")
                self.sheet = self.workbook.active
        else:
            logger.info("请先加载工作簿。")

    def read_data(self, **kwargs):
        """
        第三步:读取工作表数据
        """
        # 读取出sheet里面的信息,构建成自己想要的数据结构,然后返回结果

        data = "测试,获取到第一个excel中指定sheet的数据"
        return data

    def close_workbook(self):
        """
        最后一步:关闭工作簿,确保资源释放。
        """
        if self.workbook:
            try:
                self.workbook.close()
                logger.info(f"成功关闭工作簿:{os.path.basename(self.file_path)}")
            except Exception as e:
                logger.error(
                    f"关闭boms工作簿:{os.path.basename(self.file_path)} 时发生错误: {e} {traceback.format_exc()}")



# 获取第二个excel文件的内容 
class GetExcelFileTwoDatas:

    def __init__(self, file_path):
        self.file_path = file_path  # 用于存储excel文件路径
        self.workbook = None  # 用于存储加载的工作簿对象
        self.sheet = None  # 用于存储选择的工作表对象

    def load_workbook(self):
        """
        第一步:加载 excel 文件
        """
        try:
            self.workbook = openpyxl.load_workbook(self.file_path, keep_vba=True)
            logger.info(f"成功加载工作簿:{self.file_path}")
        except FileNotFoundError:
            logger.error(f"文件 '{self.file_path}' 未找到。")
        except Exception as e:
            logger.error(f"加载工作簿时发生错误: {e} {traceback.format_exc()}")

    def load_sheet(self, sheet_name=None):
        """
        第二步:选择并加载指定工作表。

        参数:
            sheet_name (str): 需要读取的工作表名称。
        """
        if self.workbook:
            if sheet_name is not None and sheet_name in self.workbook.sheetnames:
                self.sheet = self.workbook[sheet_name]
                logger.info(f"成功加载工作表:'{sheet_name}'")
            elif sheet_name is None:
                self.sheet = self.workbook.active
                logger.info(f"默认加载第一个工作表")
            else:
                logger.info(f"工作簿中不存在指定工作表:'{sheet_name}',默认加载第一个工作表")
                self.sheet = self.workbook.active
        else:
            logger.info("请先加载工作簿。")

    def read_data(self, **kwargs):
        """
        第三步:读取工作表数据
        """
        # 读取出sheet里面的信息,构建成自己想要的数据结构,然后返回结果

        data = "测试,获取到第二个excel中指定sheet的数据"
        return data

    def close_workbook(self):
        """
        最后一步:关闭工作簿,确保资源释放。
        """
        if self.workbook:
            try:
                self.workbook.close()
                logger.info(f"成功关闭工作簿:{os.path.basename(self.file_path)}")
            except Exception as e:
                logger.error(
                    f"关闭boms工作簿:{os.path.basename(self.file_path)} 时发生错误: {e} {traceback.format_exc()}")


def get_all_excel_data(file_path_one, file_path_two):
    # 读取第一个excel文件
    excel_one = GetExcelFileOneDatas(file_path_one)
    excel_one.load_workbook()
    excel_one.load_sheet(sheet_name="Sheet1")
    data_one = excel_one.read_data()
    excel_one.close_workbook()

    # 读取第二个excel文件
    excel_two = GetExcelFileTwoDatas(file_path_two)
    excel_two.load_workbook()
    excel_two.load_sheet(sheet_name="Sheet2")
    data_two = excel_two.read_data()
    excel_two.close_workbook()

    # 返回两个excel文件的数据
    return data_one, data_two


get_all_excel_data("file_path_one", "file_path_two")


总结

  • 使用抽象基类可以让代码更加结构化、易维护,特别适用于需要多个子类共享相同接口但逻辑不同的场景。通过强制实现抽象方法,可以确保子类接口一致,提高代码的可读性和可扩展性。

原文地址:https://blog.csdn.net/qq_29371275/article/details/144379559

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!