python: 抽象基类详解
思维导入
原始表达式:
(
A
⋅
B
1
⋅
C
)
(A \cdot B1 \cdot C)
(A⋅B1⋅C)
(
A
⋅
B
2
⋅
C
)
(A \cdot B2 \cdot C)
(A⋅B2⋅C)
(
A
⋅
B
3
⋅
C
)
(A \cdot B3 \cdot C)
(A⋅B3⋅C)
这个表达式表示三个矩阵乘法的序列,其中 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}
A⋅C⋅
B1B2B3
这里,A 和 C 首先相乘,然后将结果与一个垂直堆叠的矩阵相乘,这个垂直堆叠的矩阵由 B1、B2 和 B3 组成。这种分解可以简化计算,特别是当 A 和 C 的乘积可以被重复使用时。
通过预先计算 A*C 的结果,可以避免在每次与 不同的 B 矩阵相乘时重复计算 A 和 C 的乘积。这类似于在抽象基类中定义通用方法,以避免在多个子类中重复相同的代码,而不同的B矩阵就类似于各个子类中的重写的抽象方法;
什么是抽象基类?
在 Python 中,抽象基类(Abstract Base Class,简称 ABC)是一种特殊的类,用于定义其他类的接口,旨在提供一种强制性机制,确保子类实现预定义的方法。抽象基类不能被实例化,通过继承和扩展它们,开发者可以设计一致性强、结构明确的代码。
Python 提供了 abc 模块来支持抽象基类的创建和使用。
当我们有这样的需求的时候,抽象基类就是一个很好的选择:子类必须实现某些指定的方法和属性,否则就抛异常。
抽象基类的主要特点:
- 不能被实例化:抽象基类不能直接创建对象,尝试实例化会抛出
TypeError
。 - 包含抽象方法:抽象方法是没有实现体的方法,子类必须实现这些方法,否则它们也会变成抽象类,不能被实例化。
- 强制子类实现接口:通过定义抽象方法,你可以确保所有子类都实现了这些方法。
如何创建抽象基类:
-
导入
abc
模块:from abc import ABC, abstractmethod
-
定义抽象基类:
使用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)!