上一篇咱们深入掌握了面向对象进阶特性,通过继承实现代码高效复用,依托方法重写与多态提升代码灵活性,用访问限制筑牢数据安全防线,还基于这些特性优化了学生信息管理系统,构建了权限分级、逻辑清晰的业务模型。但美中不足的是,当前系统的所有数据都存储在内存中,一旦程序退出、重启或意外中断,所有学生信息、用户数据都会彻底丢失,无法实现长期使用。想要让程序真正具备实用价值,就必须突破内存存储的局限,实现"数据持久化"------将数据写入本地文件,让程序再次运行时能重新读取数据,真正拥有"记忆"能力。
今天咱们聚焦思维导图"文件操作与数据持久化"核心模块,从文件操作的基础流程、常用格式(文本、JSON)读写,到文件系统交互、对象数据持久化实战,再到操作规范与注意事项,逐一深入拆解。学会这些内容,你将能把面向对象程序与本地文件结合,开发出可长期存储数据的实用系统,同时为下一章"异常处理"(解决文件操作中的报错、异常场景)打下坚实基础。
一、文件操作基础:掌握"打开-操作-关闭"核心流程
文件操作是数据持久化的基石,Python提供了简洁的内置语法与函数,支持对本地文件进行创建、读取、写入、关闭等操作。无论处理哪种格式的文件,都离不开"打开文件→操作文件→关闭文件"的核心流程,这是保障文件安全、数据完整的关键。
1. 核心函数与参数:open()函数详解
对文件进行任何操作前,必须先通过`open()`函数打开文件,获取文件对象(也叫文件句柄),后续所有操作都通过该对象完成。`open()`函数的参数决定了文件的操作权限、编码方式,是文件操作的核心。
python
# open()函数基础语法
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
# 常用核心参数说明
# file:必填,文件路径(相对路径或绝对路径),字符串类型
# mode:可选,文件打开模式,决定操作类型(读/写/追加等),默认'r'
# encoding:可选,文件编码格式,推荐统一用'utf-8'(兼容中文与跨平台)
# errors:可选,编码错误处理方式,如'ignore'忽略错误、'replace'替换错误字符
2. 常用文件打开模式(实战高频)
文件打开模式决定了对文件的操作权限,错误的模式会导致操作失败(如只读模式下写入)或数据丢失(如覆盖模式下误写),以下是实战中最常用的6种模式及特性,结合场景精准选用:
| 模式 | 核心功能 | 关键特性 | 适用场景 |
|---|---|---|---|
| r | 只读模式(文本) | 文件必须存在,否则抛FileNotFoundError;仅能读取内容,指针默认在文件开头 | 读取已存在的文本文件 |
| w | 写入模式(文本) | 文件不存在则创建,存在则清空原有内容后写入;慎用,易丢失数据 | 新建文件写入,或覆盖原有文件内容 |
| a | 追加模式(文本) | 文件不存在则创建,存在则在文件末尾追加内容,不覆盖原有数据 | 日志记录、增量数据写入 |
| r+ | 读写模式(文本) | 文件必须存在,可读取也可写入,写入内容覆盖原有位置数据,指针可移动 | 修改已存在文件的指定内容 |
| wb | 写入模式(二进制) | 以字节流形式写入,无需指定encoding;文件不存在则创建,存在则清空 | 写入图片、视频、压缩包等二进制文件 |
| rb | 读取模式(二进制) | 以字节流形式读取,无需指定encoding;文件必须存在 | 读取图片、视频、压缩包等二进制文件 |
注意:文本模式(无b)适用于.txt、.json等文本文件,需指定encoding;二进制模式(加b)适用于非文本文件,无需指定encoding,操作的是字节流(bytes类型)。
3. 文件操作的两种方式(安全优先)
(1)手动打开与关闭:try-finally保障安全
手动调用`open()`获取文件对象,操作完成后必须调用`close()`关闭文件,释放系统资源。为避免操作中抛出异常导致文件无法关闭,需用`try-finally`语句包裹,确保资源释放,这是早期文件操作的标准写法。
python
# 手动打开与关闭文件(读取操作)
try:
# 打开文件,指定模式与编码
f = open("test.txt", mode='r', encoding='utf-8')
# 读取文件内容
content = f.read()
print("文件内容:", content)
finally:
# 无论是否报错,都强制关闭文件
if 'f' in locals(): # 判断文件对象是否存在
f.close()
# 手动打开与关闭文件(写入操作)
try:
f = open("test.txt", mode='w', encoding='utf-8')
f.write("Hello, 文件操作!")
finally:
if 'f' in locals():
f.close()
(2)with语句自动关闭:推荐用法
Python提供`with`语句语法糖,是文件操作的最佳实践。进入`with`代码块时自动打开文件,退出代码块时自动关闭文件(无论是否报错),无需手动调用`close()`,简化代码的同时保障安全。
python
# 读取文件(with语句)
with open("test.txt", 'r', encoding='utf-8') as f:
content = f.read()
print("读取内容:", content) # 输出:Hello, 文件操作!
# 追加内容(with语句,a模式)
with open("test.txt", 'a', encoding='utf-8') as f:
f.write("\n追加一行新内容")
# 验证结果
with open("test.txt", 'r', encoding='utf-8') as f:
print("追加后内容:")
print(f.read())
# 最终输出:
# Hello, 文件操作!
# 追加一行新内容
二、文本文件读写:基础数据持久化
文本文件(.txt)是最基础的文件格式,适用于存储简单字符串数据(如日志、配置、纯文本内容)。Python提供多种读取和写入方法,适配不同场景(全量读取、逐行读取、指定长度读取)。
1. 文本读取方法(按场景选用)
文件对象提供4种核心读取方法,各自适配不同场景,需结合文件大小和需求选择,避免内存溢出:
python
with open("test.txt", 'r', encoding='utf-8') as f:
# 1. read(size):读取指定字节数,默认读取全量内容
content1 = f.read() # 无参数,读取所有内容
print("read()全量读取:", content1)
# 重置文件指针到开头(否则指针在文件末尾,后续读取为空)
f.seek(0)
# 2. readline():读取一行内容,包含换行符\n
content2 = f.readline()
print("readline()单行读取:", content2.strip()) # strip()去除换行符与空格
# 3. readlines():读取所有行,返回列表,每行为一个元素(含\n)
f.seek(0)
content3 = f.readlines()
print("readlines()按行读取:", content3) # 输出:['Hello, 文件操作!\n', '追加一行新内容']
# 4. 迭代文件对象:逐行读取,内存友好,适合大文件
f.seek(0)
print("迭代逐行读取:")
for line in f:
print(line.strip())
核心建议:小文件(几MB以内)可用`read()`或`readlines()`;大文件(几十MB及以上)优先用"迭代文件对象"或`readline()`,避免一次性加载全量内容占用过多内存。
2. 文本写入方法(精准控制内容)
文本写入主要通过`write()`和`writelines()`两种方法,结合打开模式(w/覆盖、a/追加)使用,确保数据写入符合预期:
python
# 1. write():写入字符串,返回写入的字符数
with open("test.txt", 'w', encoding='utf-8') as f:
count = f.write("第一行内容\n第二行内容")
print("写入字符数:", count) # 输出:18(包含换行符\n)
# 2. writelines():写入可迭代对象(列表、元组等),元素需为字符串
# 注意:不会自动添加换行符,需手动在元素末尾加\n
lines = ["第三行内容\n", "第四行内容\n"]
with open("test.txt", 'a', encoding='utf-8') as f:
f.writelines(lines)
# 验证写入结果
with open("test.txt", 'r', encoding='utf-8') as f:
print("最终写入内容:")
print(f.read())
# 输出:
# 第一行内容
# 第二行内容
# 第三行内容
# 第四行内容
3. 文件指针操作:灵活定位读写位置
文件指针是文件操作中的"光标",决定了下一次读写的位置。默认情况下,打开文件后指针位置如下:读模式(r/r+)在文件开头,写模式(w/w+)和追加模式(a/a+)在文件末尾。可通过`seek()`和`tell()`方法控制指针位置。
python
with open("test.txt", 'r+', encoding='utf-8') as f:
# tell():获取当前指针位置(字节数)
print("初始指针位置:", f.tell()) # 输出:0(文件开头)
# 读取5个字符,指针后移5个字节
content = f.read(5)
print("读取内容:", content) # 输出:第一行内
print("读取后指针位置:", f.tell()) # 输出:10(utf-8中一个中文占2字节)
# seek(offset, whence):移动指针位置
# offset:偏移量(字节数);whence:基准位置(0=文件开头,1=当前位置,2=文件末尾)
f.seek(0, 0) # 指针移回文件开头
print("指针重置后位置:", f.tell()) # 输出:0
# 在开头写入内容,覆盖原有数据
f.write("替换内容")
f.seek(0)
print("最终内容:", f.read()) # 输出:替换内容容\n第二行内容...
三、JSON文件读写:结构化数据持久化
文本文件适用于简单字符串存储,而JSON(JavaScript Object Notation)文件是轻量级的结构化数据格式,适用于存储字典、列表等结构化数据(如用户信息、配置参数、学生列表),且跨语言兼容(Python、Java、JavaScript等均可解析),是实战中最常用的结构化数据持久化格式。
1. JSON模块核心函数
Python内置`json`模块,提供4个核心函数,实现JSON数据与Python数据的相互转换,以及文件读写:
| 函数 | 功能描述 | 适用场景 |
|---|---|---|
| json.dumps() | 将Python数据(字典/列表)转为JSON字符串 | 需先转换为字符串,再手动写入文件 |
| json.loads() | 将JSON字符串转为Python数据(字典/列表) | 先手动读取文件内容,再转换为Python数据 |
| json.dump() | 将Python数据直接写入JSON文件(自动转JSON字符串) | 直接持久化Python结构化数据,推荐用法 |
| json.load() | 从JSON文件直接读取数据(自动转Python数据) | 直接加载JSON文件数据,推荐用法 |
2. JSON文件写入:Python数据→JSON文件
使用`json.dump()`可直接将Python字典、列表写入JSON文件,自动完成Python数据到JSON字符串的转换,无需手动处理格式。
python
import json
# 准备Python结构化数据(字典/列表)
student_list = [
{"name": "小明", "age": 18, "score": 90, "is_pass": True},
{"name": "小红", "age": 17, "score": 92, "is_pass": True},
{"name": "小刚", "age": 18, "score": 58, "is_pass": False}
]
# 写入JSON文件
with open("students.json", 'w', encoding='utf-8') as f:
# ensure_ascii=False:允许写入中文;indent=2:格式化输出,增强可读性
json.dump(student_list, f, ensure_ascii=False, indent=2)
print("JSON文件写入成功")
生成的`students.json`文件内容(格式化后,可读性强):
python
[
{
"name": "小明",
"age": 18,
"score": 90,
"is_pass": true
},
{
"name": "小红",
"age": 17,
"score": 92,
"is_pass": true
},
{
"name": "小刚",
"age": 18,
"score": 58,
"is_pass": false
}
]
3. JSON文件读取:JSON文件→Python数据
使用`json.load()`可直接从JSON文件读取数据,自动完成JSON字符串到Python数据(字典/列表)的转换,无需手动解析。
python
import json
# 读取JSON文件
with open("students.json", 'r', encoding='utf-8') as f:
# 自动将JSON数据转为Python列表
data = json.load(f)
# 操作读取到的数据
print("读取到的学生数据:", data)
print("数据类型:", type(data)) # 输出:<class 'list'>
# 遍历学生数据
for student in data:
print(f"姓名:{student['name']},成绩:{student['score']}")
4. JSON操作注意事项
-
数据类型兼容:JSON仅支持有限数据类型,Python中的元组会转为列表,集合不支持JSON序列化,需手动转换为兼容类型(如列表);
-
中文编码问题:写入时必须指定`ensure_ascii=False`,否则中文会被转为Unicode编码(如\u5c0f\u660e),同时需指定`encoding='utf-8'`;
-
格式化输出:`indent`参数用于指定缩进空格数,增强JSON文件可读性,生产环境建议添加;
-
对象序列化:JSON无法直接序列化Python对象(如自定义的Student实例),需手动定义序列化方法(后续实战讲解)。
四、实战:学生信息管理系统数据持久化
结合本章文件操作知识,优化上一章的学生信息管理系统,实现学生数据的JSON文件持久化------程序启动时读取JSON文件加载数据,新增、修改、删除操作后同步写入JSON文件,确保数据长期保存,程序重启后不丢失。
python
import json
# 父类:用户类(封装共性)
class User(object):
def __init__(self, name, account, password):
self.name = name
self.account = account
self.__password = password # 私有密码属性
# 密码校验(私有方法)
def __verify_pwd(self, input_pwd):
return self.__password == input_pwd
# 登录接口(公有方法)
def login(self, input_pwd):
if self.__verify_pwd(input_pwd):
print(f"{self.name}({self.account})登录成功")
return True
else:
print("密码错误,登录失败")
return False
# 子类:学生类(继承User,扩展学生特性)
class Student(User):
def __init__(self, name, account, password, score):
super().__init__(name, account, password)
self.score = score # 学生特有属性
# 学生特有方法:查看成绩
def check_score(self):
print(f"{self.name}的成绩:{self.score}分")
# 转为字典(用于JSON序列化)
def to_dict(self):
return {
"name": self.name,
"account": self.account,
"password": self._User__password, # 访问私有属性(特殊场景)
"score": self.score
}
# 子类:管理员类(继承User,扩展管理权限)
class Admin(User):
def __init__(self, name, account, password):
super().__init__(name, account, password)
# 管理员特有方法:管理学生信息(新增、修改、删除)
def manage_student(self, manager, action, *args):
if action == "add":
name, age, score = args
manager.add_student(name, age, score)
elif action == "modify":
name, new_score = args
manager.modify_student(name, new_score=new_score)
elif action == "delete":
name = args[0]
manager.delete_student(name)
# 学生信息管理类(新增JSON持久化功能)
class StudentManager(object):
def __init__(self, file_path="students.json"):
self.file_path = file_path
self.student_list = self.load_data() # 初始化时加载数据
# 从JSON文件加载数据
def load_data(self):
try:
with open(self.file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
# 将字典列表转为Student对象列表
student_list = []
for item in data:
student = Student(
name=item["name"],
account=item["account"],
password=item["password"],
score=item["score"]
)
student_list.append(student)
print("数据加载成功")
return student_list
except FileNotFoundError:
print("未找到数据文件,初始化空列表")
return []
except Exception as e:
print(f"数据加载失败:{e}")
return []
# 保存数据到JSON文件
def save_data(self):
# 将Student对象列表转为字典列表
data = [student.to_dict() for student in self.student_list]
with open(self.file_path, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print("数据保存成功")
# 新增学生
def add_student(self, name, age, score):
student = Student(
name=name,
account=f"stu_{len(self.student_list)+1:03d}",
password="123456",
score=score
)
self.student_list.append(student)
self.save_data() # 新增后保存
print(f"成功添加学生:{name},默认账号:{student.account},默认密码:123456")
# 查询学生
def query_student(self, name):
for student in self.student_list:
if student.name == name:
print(f"查询结果:姓名:{student.name},账号:{student.account},成绩:{student.score}")
return student
print(f"未找到姓名为{name}的学生")
return None
# 修改学生成绩
def modify_student(self, name, new_score=None):
student = self.query_student(name)
if student and new_score:
student.score = new_score
self.save_data() # 修改后保存
print(f"成功修改{name}的成绩,当前成绩:{student.score}分")
# 删除学生
def delete_student(self, name):
for i, student in enumerate(self.student_list):
if student.name == name:
del self.student_list[i]
self.save_data() # 删除后保存
print(f"成功删除学生:{name}")
return
print(f"未找到姓名为{name}的学生,删除失败")
# 显示所有学生
def show_all_students(self):
if not self.student_list:
print("暂无学生信息")
return
print("所有学生信息:")
for i, student in enumerate(self.student_list, 1):
print(f"第{i}名:姓名:{student.name},账号:{student.account},成绩:{student.score}")
# 测试持久化系统
if __name__ == "__main__":
# 初始化管理类(自动加载数据)
manager = StudentManager()
admin = Admin("管理员", "admin", "admin123")
# 管理员登录
if admin.login("admin123"):
# 管理员操作:添加、修改、删除学生(操作后自动保存)
admin.manage_student(manager, "add", "小明", 18, 90)
admin.manage_student(manager, "add", "小红", 17, 88)
manager.show_all_students()
admin.manage_student(manager, "modify", "小红", 92)
admin.manage_student(manager, "delete", "小明")
manager.show_all_students()
# 学生登录并查看成绩
student = manager.query_student("小红")
if student.login("123456"):
student.check_score()
案例分析
-
持久化核心:通过`load_data()`和`save_data()`方法实现数据读写,初始化时加载JSON文件数据,增删改操作后同步保存,确保数据不丢失;
-
对象序列化:自定义`to_dict()`方法将Student对象转为字典,解决JSON无法直接序列化Python对象的问题,读取时再将字典转为对象;
-
容错处理:加载数据时捕获`FileNotFoundError`(文件不存在),避免程序报错,提升稳定性;
-
衔接面向对象:与上一章的面向对象特性无缝结合,保留继承、访问限制等特性,同时新增持久化能力,让系统具备实用价值。
五、文件操作避坑指南
-
编码统一:所有文本文件操作统一使用`utf-8`编码,避免中文乱码;二进制文件操作不加encoding,操作字节流;
-
模式慎用:谨慎使用`w`模式(覆盖写入),避免误删数据;增量写入优先用`a`模式;
-
资源释放:始终用`with`语句操作文件,避免忘记关闭文件导致资源泄露;
-
路径正确:相对路径以程序运行目录为基准,绝对路径需写全(如Windows:`C:/test.txt`,Linux/Mac:`/home/test.txt`);
-
大文件处理:读取大文件时优先用迭代器逐行读取,避免`read()`一次性加载全量内容导致内存溢出。
结尾:文件操作搞定了,该学"异常处理:提升程序稳定性"了
今天咱们彻底掌握了文件操作与数据持久化的核心内容,从基础的"打开-操作-关闭"流程,到文本、JSON文件的读写,再到实战中实现学生信息系统的持久化,让程序具备了长期存储数据的"记忆"能力。现在你已经能将面向对象程序与本地文件结合,开发出实用的业务系统。
但目前的文件操作仅做了简单的容错处理,实际开发中会遇到各种异常场景:文件不存在、权限不足、编码错误、JSON格式非法等,这些都会导致程序崩溃。下一章咱们就进入"异常处理"模块,学习`try-except`、`raise`等异常处理语法,捕获并处理文件操作及程序运行中的报错,自定义异常类型,让程序在异常场景下也能稳定运行,提升代码的健壮性和可靠性。如果今天的JSON序列化、文件路径使用有疑问,随时告诉我,咱们再拆解一遍!