恭喜你完成了第三天的挑战!你已经学会了用字典组织数据,用异常处理保护程序。但你可能发现了一个问题:每次关闭程序,那些珍贵的“点播记录”就消失了。今天,我们将学习如何把数据永久地保存在硬盘上,让你的程序拥有“记忆”。
今日目标:掌握文件读写 & 让数据持久化 & 升级项目
项目实战:为“个性化名言图书馆”添加“记忆”功能
我们将升级第三天的“名言图书馆”项目,增加两个核心能力:
程序启动时:自动从硬盘文件加载历史记录。
程序运行时:每次添加新记录,不仅存入内存列表,也追加写入到文件。
数据格式:学习使用最简单的CSV格式(逗号分隔值)存储数据,它可以用Excel打开查看。
通过这个升级,你将彻底理解程序数据如何在关闭后“幸存”。
1. 项目分析与知识准备
核心概念:文件读写:Python使用内置的open()函数与文件“对话”。你需要理解读模式('r')、写模式('w') 和追加模式('a') 的区别。特别是'w'模式会覆盖原文件,要小心使用!
数据格式:CSV:一种通用的、简单的表格数据格式。每条记录占一行,字段之间用逗号分隔。例如:日期,名字,心情,名言。
新模块:csv:Python内置的csv模块能让你更方便地读写CSV文件,自动处理逗号、引号等复杂情况。
路径问题:简单了解文件保存在哪里(通常与你的.py文件在同一目录下)。
2. 编写代码:升级你的 quote_library.py
打开你第三天的文件 quote_library.py,我们将对其进行改造。
第一步:导入新模块,定义文件名字
在文件最开头,导入csv模块,并定义我们将要使用的文件名。
python
import randomfrom datetime import datetimeimport csv # <--- 新导入的模块# 文件名常量HISTORY_FILE ='quote_history.csv'# <--- 数据将保存在这里# 名言库 (保持不变)quotes =["专注是成功的秘诀。","行动是治愈恐惧的良药。",# ... 其他名言]# 注意:程序启动时,history列表将不再为空,而是从文件中加载!history =[]
第二步:编写两个新函数:加载与保存
我们将创建两个函数,专门负责与CSV文件打交道。
python
defload_history_from_file():""" 程序启动时调用,从CSV文件加载历史记录到history列表。 如果文件不存在,则忽略并返回空列表。 """ loaded_history =[]try:# 尝试以读模式打开文件withopen(HISTORY_FILE, mode='r', encoding='utf-8')asfile:# 使用csv.DictReader可以方便地将每一行读取为字典 reader = csv.DictReader(file)for row in reader:# row是一个字典,键为CSV的第一行(表头) loaded_history.append(row)print(f"✅ 已从 {HISTORY_FILE} 加载 {len(loaded_history)} 条历史记录。")except FileNotFoundError:# 如果文件不存在(比如第一次运行),这不算错误,忽略即可print(f"📭 未找到历史记录文件 {HISTORY_FILE},将创建新文件。")except Exception as e:print(f"⚠️ 加载历史记录时出现意外错误:{e}")return loaded_historydefsave_record_to_file(record):""" 每当有新记录时调用,将一条新记录追加到CSV文件中。 """ file_exists =Falsetry:# 检查文件是否存在,以决定是否需要写入表头withopen(HISTORY_FILE, mode='r', encoding='utf-8')as f: file_exists =Trueexcept FileNotFoundError: file_exists =Falsetry:# 以追加模式 ('a') 打开文件,新内容会加在末尾,不会覆盖旧内容withopen(HISTORY_FILE, mode='a', encoding='utf-8', newline='')asfile:# 定义CSV的列名,必须与字典的键一致 fieldnames =['date','name','mood','quote'] writer = csv.DictWriter(file, fieldnames=fieldnames)# 如果文件是新创建的(不存在),则先写入表头ifnot file_exists: writer.writeheader()# 写入新记录(字典格式) writer.writerow(record)print(f"💾 记录已追加到文件 {HISTORY_FILE}")except Exception as e:print(f"❌ 保存记录到文件时出错:{e}")第三步:修改主程序逻辑,整合文件操作
我们需要在程序启动时加载数据,并在保存记录时同时更新文件和内存列表。
python
defmain():print("🌟 欢迎来到个性化名言图书馆 (持久化版) 🌟")# --- 程序启动:从文件加载历史记录到全局history列表 ---global history # 声明我们要修改全局的history变量 history = load_history_from_file()# <--- 新增的加载步骤whileTrue:# ... (菜单显示代码保持不变) ...print("\n请选择功能:")print("1. 获取专属名言")print("2. 查看历史记录")print("3. 退出程序") choice =input("请输入你的选择 (1/2/3):")if choice =='1':try: name =input("请输入你的名字:").strip()if name =='':print("❌ 名字不能为空哦。")continue mood =input("你今天的心情怎么样?: ").strip() selected_quote = get_random_quote() personalized_message =f"✨ {name} 同学,看到你{mood}的样子,送你一句话:\"{selected_quote}\""print("\n"+"="*50)print(personalized_message)print("="*50)# --- 修改保存逻辑:先保存到文件,再更新内存列表 --- now = datetime.now().strftime("%Y-%m-%d %H:%M") new_record ={"date": now,"name": name,"mood": mood,"quote": selected_quote}# 1. 保存到文件 (核心新增) save_record_to_file(new_record)# 2. 更新内存中的history列表,方便本次运行中查看 history.append(new_record)print("✅ 记录已在内存和文件中保存!")# ------------------------------------------except Exception as e:print(f"❌ 处理时出现了一个意外错误:{e}")elif choice =='2':# --- 修改查看逻辑:现在直接从内存的history列表读取即可 ---# 因为history列表已经在启动时加载,且在添加时同步更新ifnot history:print("📭 目前还没有任何历史记录。")else:print("\n📜 历史点播记录 (来自内存+文件):")for i, record inenumerate(history,1): line ="{0}. [{1}] {2} ({3}) 点播了:{4}".format( i, record['date'], record['name'], record['mood'], record['quote'])print(line)elif choice =='3':print("👋 谢谢使用,再见!")breakelse:print("❌ 无效的选择,请输入 1、2 或 3。")# 确保只有直接运行这个脚本时才会执行main函数if __name__ =="__main__":# 注意:history是全局变量,这里不需要再传参 main()3. 运行与测试:见证“记忆”的力量!
首次运行:运行代码,你应该会看到提示“未找到历史记录文件,将创建新文件”。获取几条新名言。
检查文件:在你代码的同一文件夹下,找到新生成的 quote_history.csv 文件。用记事本或Excel打开它,你会看到整齐的表格数据。
再次运行:关闭并重新运行程序。这次启动时会看到“已从...加载...条历史记录”。直接选择功能2“查看历史记录”,你会发现上次关闭前的记录都还在!
今日核心知识拆解
1. 文件操作的“三步走”与with语句
在Python中操作文件有一个标准模式,而with语句是处理它的最佳实践,它能自动管理文件的打开和关闭。
python
# 标准三步走:打开 -> 读取/写入 -> 关闭# 使用 with 语句,关闭是自动的,更安全withopen('我的文件.txt','r', encoding='utf-8')as f: content = f.read()# 在缩进块内操作文件# 缩进结束,文件自动关闭模式:
'r' (read): 读取,文件必须存在。
'w' (write): 写入,会覆盖原文件,不存在则创建。
'a' (append): 追加,在文件末尾添加内容,不存在则创建。
'r+', 'w+', 'a+': 读写模式(同时可读可写),但行为各有不同,初期先掌握前三种。
2. CSV模块:处理表格数据的利器
手动用字符串分割逗号来处理CSV很容易出错(比如名言里本身就有逗号)。csv模块为我们处理了这些细节。
csv.DictReader(f):将文件每一行自动转换为一个字典,字典的键来自文件的第一行(表头)。
csv.DictWriter(f, fieldnames=...):创建一个写入器,配合writerow()可以方便地将字典写入文件。writeheader()会根据fieldnames写入表头。
3. 程序数据与持久化数据
今天你第一次区分了程序的两个数据状态:
优秀的程序会巧妙地结合两者:运行时在内存中操作以提高速度,关键节点或在程序关闭时将数据保存到硬盘。
今日总结与后续建议
恭喜你!你的程序从此拥有了“长期记忆”。今天你不仅学会了文件读写和CSV操作,更重要的是理解了数据持久化的核心思想。
你已经掌握了Python编程的四大核心支柱:
数据结构 (列表、字典) - 组织数据
流程控制 (条件、循环) - 控制逻辑
函数 - 组织代码
文件IO - 持久化数据
明日的学习方向:
面向对象编程入门:你会发现我们一直在传递数据(如record字典)和调用操作数据的函数。面向对象(OOP)允许我们将数据和操作封装成一个整体——对象。比如,我们可以创建一个QuoteRecord类,它既有date, name等属性,也有一个save_to_file的方法。这会让代码结构更清晰,尤其适合大型项目。
异常处理的进阶:学习如何捕获特定类型的异常(如FileNotFoundError),而不是笼统的Exception,这能让你的错误处理更精准。
挑战任务:为你的“名言图书馆”增加新功能:
删除记录:允许用户根据序号删除某条记录。你需要操作内存中的history列表,并重写整个文件(小心使用'w'模式)。
统计功能:显示当前一共保存了多少条名言,或者统计哪位“同学”点播次数最多。这需要你读取并分析CSV文件的数据。
导出为文本:新增一个菜单选项,将格式漂亮的历史记录导出到一个新的.txt文件中。
你已经从一个代码执行者,成长为一个能设计数据结构、处理异常、并实现数据持久化的初级软件开发者了。这个跨越非常了不起!如果在尝试中遇到任何问题,或者对明天的“面向对象”方向感到好奇,随时来和我讨论。加油!