第十五节:文件操作与数据持久化:让程序拥有“记忆”

上一篇咱们深入掌握了面向对象进阶特性,通过继承实现代码高效复用,依托方法重写与多态提升代码灵活性,用访问限制筑牢数据安全防线,还基于这些特性优化了学生信息管理系统,构建了权限分级、逻辑清晰的业务模型。但美中不足的是,当前系统的所有数据都存储在内存中,一旦程序退出、重启或意外中断,所有学生信息、用户数据都会彻底丢失,无法实现长期使用。想要让程序真正具备实用价值,就必须突破内存存储的局限,实现"数据持久化"------将数据写入本地文件,让程序再次运行时能重新读取数据,真正拥有"记忆"能力。

今天咱们聚焦思维导图"文件操作与数据持久化"核心模块,从文件操作的基础流程、常用格式(文本、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()

案例分析

  1. 持久化核心:通过`load_data()`和`save_data()`方法实现数据读写,初始化时加载JSON文件数据,增删改操作后同步保存,确保数据不丢失;

  2. 对象序列化:自定义`to_dict()`方法将Student对象转为字典,解决JSON无法直接序列化Python对象的问题,读取时再将字典转为对象;

  3. 容错处理:加载数据时捕获`FileNotFoundError`(文件不存在),避免程序报错,提升稳定性;

  4. 衔接面向对象:与上一章的面向对象特性无缝结合,保留继承、访问限制等特性,同时新增持久化能力,让系统具备实用价值。

五、文件操作避坑指南

  • 编码统一:所有文本文件操作统一使用`utf-8`编码,避免中文乱码;二进制文件操作不加encoding,操作字节流;

  • 模式慎用:谨慎使用`w`模式(覆盖写入),避免误删数据;增量写入优先用`a`模式;

  • 资源释放:始终用`with`语句操作文件,避免忘记关闭文件导致资源泄露;

  • 路径正确:相对路径以程序运行目录为基准,绝对路径需写全(如Windows:`C:/test.txt`,Linux/Mac:`/home/test.txt`);

  • 大文件处理:读取大文件时优先用迭代器逐行读取,避免`read()`一次性加载全量内容导致内存溢出。

结尾:文件操作搞定了,该学"异常处理:提升程序稳定性"了

今天咱们彻底掌握了文件操作与数据持久化的核心内容,从基础的"打开-操作-关闭"流程,到文本、JSON文件的读写,再到实战中实现学生信息系统的持久化,让程序具备了长期存储数据的"记忆"能力。现在你已经能将面向对象程序与本地文件结合,开发出实用的业务系统。

但目前的文件操作仅做了简单的容错处理,实际开发中会遇到各种异常场景:文件不存在、权限不足、编码错误、JSON格式非法等,这些都会导致程序崩溃。下一章咱们就进入"异常处理"模块,学习`try-except`、`raise`等异常处理语法,捕获并处理文件操作及程序运行中的报错,自定义异常类型,让程序在异常场景下也能稳定运行,提升代码的健壮性和可靠性。如果今天的JSON序列化、文件路径使用有疑问,随时告诉我,咱们再拆解一遍!

相关推荐
是三好2 小时前
JUC并发编程
java·开发语言
qq_423233902 小时前
实战:用Python开发一个简单的区块链
jvm·数据库·python
哈哈不让取名字2 小时前
分布式日志系统实现
开发语言·c++·算法
3GPP仿真实验室2 小时前
【MATLAB源码】6G:感知辅助毫米波 MIMO 信道估计仿真平台
开发语言·matlab·智能电视
catchadmin2 小时前
Laravel12 + Vue3 的免费可商用 PHP 管理后台 CatchAdmin V5.1.1 发布
开发语言·php
s石有八九2 小时前
PDF/文档LLM作业批改:2025年研究现状、技术进展与研究空白
人工智能·python·pdf·教育·ai教育·作业批改
编程(变成)小辣鸡3 小时前
JVM、JRE和JDK 的关系
java·开发语言·jvm
岱宗夫up3 小时前
基于ROS的视觉导航系统实战:黑线循迹+激光笔跟随双模态实现(冰达机器人Nano改造)
linux·python·机器人·ros
a程序小傲3 小时前
得物Java面试被问:流批一体架构的实现和状态管理
java·开发语言·数据库·redis·缓存·面试·架构