python中的抽象类在项目中的实际应用
抽象类在项目中的实际应用主要体现在 规范代码结构、强制子类实现某些方法、提供部分通用功能,让代码更稳定、易维护。
举个例子:数据校验器
假设你在做一个 用户输入校验系统,需要支持 数字校验、字符串校验 和 邮箱校验。如果不用抽象类,每个校验器都会自己定义 validate() 方法,开发者可能忘了统一标准,导致代码不一致。
用抽象类改进
from abc import ABC, abstractmethodclass Validator(ABC): """抽象类:所有校验器的基类,规定必须有 validate 方法"""@abstractmethoddef validate(self, value):"""子类必须实现这个方法,否则会报错"""pass class NumberValidator(Validator): """数字校验器"""def validate(self, value):return isinstance(value, (int, float)) # 只接受数字class EmailValidator(Validator): """邮箱校验器"""def validate(self, value):return isinstance(value, str) and "@" in value # 简单的邮箱格式检查# 试试用这些校验器:
num_validator = NumberValidator()
print(num_validator.validate(123)) # True
print(num_validator.validate("abc")) # Falseemail_validator = EmailValidator()
print(email_validator.validate("test@example.com")) # True
print(email_validator.validate(123)) # False
为什么用抽象类?
-
强制所有校验器实现
validate()Validator规定了必须有validate(),如果子类忘记写,Python 会报错,避免代码不完整:
class BadValidator(Validator):pass # 忘了写 validate 方法obj = BadValidator() # TypeError: Can't instantiate abstract class BadValidator with abstract method validate -
代码更清晰,扩展更方便
- 如果以后要加 日期校验器 之类的,只需要继承
Validator并实现validate(),不会影响现有代码:
class DateValidator(Validator):def validate(self, value):return isinstance(value, str) and "-" in value # 简单日期格式检查 - 如果以后要加 日期校验器 之类的,只需要继承
-
减少重复代码,提供通用功能
- 抽象类不仅能定义“必须实现的方法”,还能提供公共方法:
class Validator(ABC):@abstractmethoddef validate(self, value):passdef is_valid(self, value):"""包装一下 validate 方法,返回 True/False"""try:return self.validate(value)except:return False
总结
- 抽象类 是“半成品”类,不能直接用,必须让子类去填充关键方法(比如
validate())。 - 强制子类实现关键方法,避免开发者忘记写,导致功能缺失。
- 让代码结构清晰,团队合作时,每个校验器都符合相同规则,方便扩展。
- 可以提供部分通用功能,减少重复代码。
举个例子:日志记录系统
假设你在开发一个日志系统,需要支持 控制台日志、文件日志 和 数据库日志。
如果不用抽象类,每个日志处理类可能命名、方法都不一样,导致代码风格混乱、调用方式不统一。
不用抽象类(混乱)
class ConsoleLogger:def log_message(self, msg):print(f"Console: {msg}")class FileLogger:def write_log(self, msg):with open("log.txt", "a") as f:f.write(f"File: {msg}\n")class DatabaseLogger:def save(self, msg):print(f"Saving '{msg}' to database...")
问题:
- 每个日志类的方法名字不一样 (
log_message()、write_log()、save()),调用时不统一。 - 如果有人写了
NetworkLogger,他可能又起个新名字,比如send_log(),代码越来越乱。
用抽象类改进
from abc import ABC, abstractmethodclass Logger(ABC):"""抽象类:所有日志类的基类"""@abstractmethoddef log(self, msg):"""子类必须实现这个方法"""pass class ConsoleLogger(Logger):"""控制台日志"""def log(self, msg):print(f"Console: {msg}")class FileLogger(Logger):"""文件日志"""def log(self, msg):with open("log.txt", "a") as f:f.write(f"File: {msg}\n")class DatabaseLogger(Logger):"""数据库日志"""def log(self, msg):print(f"Saving '{msg}' to database...")# 统一调用方式
def process_logs(logger: Logger, message: str):"""统一的日志处理逻辑"""logger.log(message)# 使用不同日志处理器
console_logger = ConsoleLogger()
file_logger = FileLogger()
db_logger = DatabaseLogger()process_logs(console_logger, "This is a console log.")
process_logs(file_logger, "This is a file log.")
process_logs(db_logger, "This is a database log.")
为什么用抽象类?
-
所有日志类的方法名保持一致
- 统一用
log(msg),不管是控制台、文件还是数据库,代码调用方式完全一样,方便维护和扩展。
- 统一用
-
强制子类实现
log()方法- 如果开发者新写
NetworkLogger但忘记实现log(),Python 会报错,防止功能缺失:
class NetworkLogger(Logger):pass # 忘了写 log 方法logger = NetworkLogger() # TypeError: Can't instantiate abstract class NetworkLogger with abstract method log - 如果开发者新写
-
新增日志方式更方便
- 以后要加 云日志、远程 API 日志 之类的,只需继承
Logger并实现log(),不会影响现有代码:
class CloudLogger(Logger):def log(self, msg):print(f"Sending '{msg}' to cloud storage...") - 以后要加 云日志、远程 API 日志 之类的,只需继承
-
提高代码可读性,减少混乱
- 团队成员看到
Logger抽象类,就知道所有日志类都有log()方法,而不会有人随便起不同的方法名。
- 团队成员看到
总结
- 抽象类 让所有日志类的接口统一,防止命名混乱。
- 强制子类实现
log()方法,防止开发者漏写关键功能。 - 以后要新增日志类型,只需要继承
Logger并实现log()方法,不影响其他代码。 - 调用方式一致,使用
process_logs(logger, msg)处理不同日志,避免各种log_message()、write_log()、save()的混乱。
在这里你可能又有一个疑问

是否可以理解成抽象类其实就是一个父类?
是的,可以理解成 “抽象类就是一个特殊的父类”,但是相比普通父类,它有以下 两点关键区别:
-
不能直接实例化(不能直接创建对象)
- 例如
Logger只是个抽象概念,不能Logger()直接用,而是用它的子类(ConsoleLogger、FileLogger)。
logger = Logger() # ❌ 报错:TypeError: Can't instantiate abstract class Logger - 例如
-
必须让子类实现某些方法
- 抽象类里用
@abstractmethod标记的方法,子类必须重写,否则 Python 不允许创建子类的实例:
class Logger(ABC):@abstractmethoddef log(self, msg):pass # 这个方法必须由子类实现 - 抽象类里用
普通父类 vs 抽象类
| 特性 | 普通父类 | 抽象类 |
|---|---|---|
| 能否直接创建对象? | ✅ 可以 | ❌ 不能 |
| 是否强制子类实现方法? | ❌ 不强制 | ✅ 强制 |
简单来说:
- 普通父类更像是可以直接用的工具箱,你可以直接拿来创建对象。
- 抽象类更像是一张设计图,告诉子类“必须实现哪些方法”,但不能直接用。
如果只是提供通用功能(比如 super().__init__() 共享构造逻辑),用普通父类就够了。
如果是设计一个必须让子类实现某些功能的模板,那就用抽象类。
你可以把抽象类想象成“公司规定”——
“所有员工(子类)都必须打卡(实现
log()方法)!”
“但是规定本身(Logger抽象类)不能当员工。”
那么我们又如何从代码上区分一个类是抽象类还是普通的父类?
从代码上区分 抽象类 和 普通父类,主要看以下几个特征:
1. 是否继承 ABC
- 抽象类 必须继承
ABC(Abstract Base Class),普通父类不需要。
from abc import ABC, abstractmethodclass AbstractClass(ABC): # ✅ 抽象类,继承 ABCpassclass NormalClass: # ❌ 普通父类,没有继承 ABCpass
2. 是否有 @abstractmethod
- 抽象类 至少有一个
@abstractmethod装饰的方法,普通父类没有。
from abc import ABC, abstractmethodclass AbstractClass(ABC):@abstractmethoddef required_method(self):pass # 这个方法必须让子类实现class NormalClass:def normal_method(self):pass # 这个方法可选,子类不一定要实现
3. 能否直接实例化
- 抽象类 不能 直接实例化,普通父类可以。
abstract_obj = AbstractClass() # ❌ TypeError: 不能实例化抽象类
normal_obj = NormalClass() # ✅ 可以实例化普通父类
4. 是否强制子类实现某些方法
- 抽象类 强制 子类必须实现
@abstractmethod方法,否则不能实例化子类。
class ConcreteClass(AbstractClass): # 继承抽象类passobj = ConcreteClass() # ❌ TypeError: 子类没实现 required_method,无法实例化
总结:快速判断方法
| 判断方法 | 普通父类 | 抽象类 |
|---|---|---|
是否继承 ABC? | ❌ 否 | ✅ 是 |
是否有 @abstractmethod? | ❌ 没有 | ✅ 有 |
| 能否直接创建对象? | ✅ 可以 | ❌ 不行 |
| 子类是否必须实现某些方法? | ❌ 不强制 | ✅ 强制 |
如果一个类符合 继承 ABC 且有 @abstractmethod,那它就是 抽象类!
否则,它就是 普通父类。
相关文章:
python中的抽象类在项目中的实际应用
抽象类在项目中的实际应用主要体现在 规范代码结构、强制子类实现某些方法、提供部分通用功能,让代码更稳定、易维护。 举个例子:数据校验器 假设你在做一个 用户输入校验系统,需要支持 数字校验、字符串校验 和 邮箱校验。如果不用抽象类&…...
New Game--(单调队列)
I - New Game 有一种新的游戏,Monocarp 想要玩。这个游戏使用一副包含 n 张牌的牌堆,其中第 i 张牌上写有一个整数 a_i。 在游戏开始时,Monocarp 可以在第一轮选择牌堆中的任意一张牌。在接下来的每一轮中,Monocarp 可以选择一张…...
mapbox V3 新特性,添加下雪效果
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象…...
无人机遥感在农林信息提取中的实现方法与GIS融合制图教程
遥感技术作为一种空间大数据手段,能够从多时、多维、多地等角度,获取大量的农情数据。数据具有面状、实时、非接触、无伤检测等显著优势,是智慧农业必须采用的重要技术之一。 一:综合态势分析 1.1 研究区及作物品种分析 ÿ…...
生物发酵展与2025生物医药创新技术与应用发展论坛同期盛大举办
近日,备受瞩目的生物发酵展与2025生物医药创新技术与应用发展论坛暨展览会宣布将同期盛大举办。这一消息标志着生物科技领域两大盛会的强强联合,将为全球生物科技与医药行业带来前所未有的交流与合作机遇。 生物发酵展作为生物科技领域的知名展会&#x…...
Jenkins 配置 Git Repository 五
Jenkins 配置 Git Repository 五 这里包含了 Freestyle project 任务类型 和 Pipeline 任务类型 关于 Git 仓库的配置,如下 不同的任务类型,只是在不同的模块找到 配置 Git 仓库 找到 Git 仓库配置位置之后,所有的任务类型配置都是一样的 …...
记录阿里云CDN配置
网站接入CDN全流程,共4步!-阿里云开发者社区 1、开通阿里云CDN服务 2、添加加速域名 3、验证域名归属权 4、域名添加CDN生成的CNAME解析 按照官网描述增加。细节点: 1. 域名和泛域名区别 2.开启https,要用nginx的证书,和项…...
mapbox 从入门到精通 - 目录
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀总目录1.1 ☘️ mapbox基础1.2 ☘️…...
mysql中general_log日志详解
介绍 1.记录范围:这个log里面会记录MySQL所有的SQL语句,不管是查询语句,还是DML语句,还是DDL语句,还是DCL语句,这些语句统统都会被记录在general log文件中。就连我们连接和断开MySQL数据库的这些语句。 2…...
算法与数据结构:从基础到深入
1. 数组 (Array) 定义 一组连续内存空间存储的相同类型元素的集合。特点:通过下标(索引)快速访问元素,但大小固定(静态数组)或可扩展(动态数组)。 核心操作 操作时间复杂度说明访…...
基于千兆5G网关的5G急救车方案
伴随5G网络的全面建成,5G技术的低延时、高速率、广接入等优势,为各行各业都带来了新一轮技术升级。在医疗救援方面,救护车是链接病患与医院的重要纽带,得益于5G物联网的融合应用,救护车也快速向联网化、信息化、智能化…...
【C#】的WPF或是WinForm实现Ctrl+ 的快捷键组合使用
在C#中,无论是WPF还是WinForms应用程序,处理快捷键(例如 Ctrl )通常涉及检测键盘输入并执行相应的命令或方法。 WPF 实现 在WPF中,可以通过设置一个控件的 InputBindings 属性来绑定快捷键。 <Window x:Class&qu…...
c语言样式主题 清爽风格 代码色彩 keil风格 适合单片机开发GD32 STM32等 cursor或者vscode 的settings.json文件
c语言样式主题 清爽风格 代码色彩 keil风格 适合单片机开发GD32 STM32等 cursor或者vscode 的settings.json文件 如上图,是不是和keil mdk很相近。 代码色彩,简单,配合 // 设置工作台主题为 Visual Studio 2017 Light - C 主题使用…...
DeepSeek API 调用 - Spring Boot 实现
DeepSeek API 调用 - Spring Boot 实现 1. 项目依赖 在 pom.xml 中添加以下依赖: <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></depe…...
图数据库Neo4j面试内容整理-节点(Node)
在图数据库中,节点(Node)是图结构中的基本构建块,代表实体或对象。节点通常用于存储数据模型中的主要对象,比如人、商品、地点等。在图数据库中,节点是通过标签(Label)来分类的,并且可以包含属性(Property)来描述它们的详细信息。 1. 节点的组成<...
使用verilog 实现 cordic 算法 ----- 旋转模式
1-设计流程 ● 了解cordic 算法原理,公式,模式,伸缩因子,旋转方向等,推荐以下链接视频了解 cordic 算法。哔哩哔哩-cordic算法原理讲解 ● 用matlab 或者 c 实现一遍算法 ● 在FPGA中用 verilog 实现,注意…...
2.14寒假
这几天复习的搜索把之前做过的题目看了一下。 解析:int dx[5]{0,0,1,0,-1}; 和 int dy[5]{0,1,0,-1,0};:这两个数组用于表示上下左右四个方向的偏移量,方便在 DFS 中访问相邻的元素。o 和 p 分别表示当前搜索位置的行和列。边界条件判断&…...
基于逻辑概率的语义信道容量(Semantic Channel Capacity)和语义压缩理论(Semantic Compression Theory)
基于逻辑概率的语义信道容量(Semantic Channel Capacity)和语义压缩理论(Semantic Compression Theory)是语义通信(Semantic Communication, SemCom)的核心研究方向,它们旨在优化通信效率&#…...
DeepSeek R1本地部署教程
尽管许多卖课博主声称能轻松运行满血版DeepSeek R1,但满血版R1模型参数高达671B,仅模型文件就需要404GB存储空间,运行时更需要约1300GB显存。 对于没有卡的普通玩家来说,运行的条件苛刻,且门槛极高。基于此࿰…...
CEF132编译指南 MacOS 篇 - 获取 CEF 源码 (五)
1. 引言 在完成了所有必要工具的安装和配置之后,我们正式进入获取 CEF132 源码的阶段。对于 macOS 平台,CEF 的源码获取过程需要特别注意不同芯片架构(Intel 和 Apple Silicon)的区别以及版本管理。本篇将作为 CEF132 编译指南系…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
