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 编译指南系…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
windows系统MySQL安装文档
概览:本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容,为学习者提供全面的操作指导。关键要点包括: 解压 :下载完成后解压压缩包,得到MySQL 8.…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...
如何在Windows本机安装Python并确保与Python.NET兼容
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)
名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 原创笔记:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 上一篇:《数据结构第4章 数组和广义表》…...
Linux信号保存与处理机制详解
Linux信号的保存与处理涉及多个关键机制,以下是详细的总结: 1. 信号的保存 进程描述符(task_struct):每个进程的PCB中包含信号相关信息。 pending信号集:记录已到达但未处理的信号(未决信号&a…...
C#学习12——预处理
一、预处理指令: 解释:是在编译前由预处理器执行的命令,用于控制编译过程。这些命令以 # 开头,每行只能有一个预处理指令,且不能包含在方法或类中。 个人理解:就是游戏里面的备战阶段(不同对局…...
