一、本篇摘要

还记得 Day 7 猜数字游戏吗?我们用列表 history = [] 记录了每局战绩,很漂亮——但有个致命遗憾:程序一关闭,战绩全丢。今天我们就来解决这个问题——教会 Python “记住昨天”。

本篇双向对照 Scratch 列表导入/导出 ↔ Python 的 open/read/write/with 全套语法,最后用一个AI 问答训练集管理工具串联所有知识点。学完这一篇,孩子就能理解:AI 大模型每天吞的那些"训练数据",本质就是一个个能被打开、被读写的文件

二、本节课学习目标

  1. 掌握 open() 函数的三种基础模式——r / 写 w / 追加 a,理解 with 语句为什么是"必选项"。
  2. 学会逐行读for line in f)和一次性读f.read()),理解两者的使用场景差异。
  3. 掌握 CSV 表格格式读写,理解 AI 训练数据集(问答对 / 意图分类 / 语料标注)的存储原理。
  4. 完成一个可实际使用的 AI 问答训练集管理工具,理解"数据持久化"是所有 AI 应用的地基。

三、专业名词通俗释义

专业名词 通俗解释
文件 硬盘上的一段数据,.txt .csv .json 都是文件
路径 文件在电脑里的"住址",如 D:/data/notes.txt 或相对路径 ./notes.txt
打开模式 r 读 / w 写(覆盖) / a 追加 / b 二进制
编码 中文文件必须指定 encoding="utf-8",否则容易乱码
CSV 用逗号分隔的表格文件,ExcelWPSNumbers 都能直接打开
with 语句 Python "打开-用完自动关"的神器,避免忘记 close() 导致数据丢失

四、分模块双向对照知识点

模块 1:Scratch 数据 vs Python 文件——为什么要学文件?

Scratch 现状:项目里的变量、列表在关闭作品的瞬间就消失。想保留数据?只能右键列表 → “导出” → 存成 .txt。
在这里插入图片描述
在这里插入图片描述

Python 优势open() 一行代码就能把内存里的数据写进硬盘长期保存

核心区别

对比项 Scratch Python
数据保存 内存里,关闭即消失 文件里,硬盘长期保留
操作方式 右键列表菜单手动导入/导出 代码自动读写
应用场景 玩游戏、动画演示 AI 训练数据、日志、配置、存档

在这里插入图片描述

模块 2:文件写入(w 模式)——把数据"存到硬盘"

在这里插入图片描述

在这里插入图片描述

# 【Scratch积木】新建列表 [chat_log]
# 【Scratch积木】将 "你好" 加入 [chat_log]
# 【Scratch积木】右键列表 → 导出 chat_log.txt

with open("chat_log.txt", "w", encoding="utf-8") as f:
    f.write("你好\n")
    f.write("在吗\n")
    f.write("今天天气不错\n")

print("✅ 写入完成")

运行结果:当前目录出现 chat_log.txt,内容为三行文本。

核心易错说明

  • "w" 模式是覆盖写——同名文件里的原有内容会被全部清空再写,一定要想清楚再用!
  • \n 是换行符,不写就会全部挤在一行
  • 中文文件必须写 encoding="utf-8",Windows 电脑默认 GBK 会乱码。

模块 3:文件读取(r 模式)——把数据"读回内存"

在这里插入图片描述

# 【Scratch积木】遍历列表 [chat_log],说出每一项

# 方式 A:逐行读(推荐,省内存)
with open("chat_log.txt", "r", encoding="utf-8") as f:
    for line in f:
        print("读取:", line.strip())

# 方式 B:一次性读全部
with open("chat_log.txt", "r", encoding="utf-8") as f:
    content = f.read()
    print(content)

# 方式 C:读成列表(每行一个元素)
with open("chat_log.txt", "r", encoding="utf-8") as f:
    lines = f.readlines()
    print(lines)   # ['你好\n', '在吗\n', '今天天气不错\n']

运行结果:三种方式都能读到内容,逐行读适合大文件,一次性读适合小配置。

核心易错说明

  • line.strip() 会去掉每行末尾的 \n 和多余空格,几乎每次读文件都要用一次。
  • 文件不存在会报 FileNotFoundError——大项目里要先用 os.path.exists() 判断。

模块 4:追加模式(a 模式)——只加新数据,不动老数据

在这里插入图片描述

# 【Scratch积木】将 "新消息" 加入列表末尾(不清空原有)

with open("chat_log.txt", "a", encoding="utf-8") as f:
    f.write("这是追加的新消息\n")
    f.write("这也是新加的\n")

print("✅ 追加完成,老数据完好无损")

运行结果:原有 3 行 + 新加 2 行 = 共 5 行。

核心易错说明

  • aw 的区别是新手最容易混的:w 一开就清空,a 一开就在末尾续写
  • 记录日志、聊天历史、战绩存档——永远用 a

模块 5:CSV 表格读写——AI 训练数据集的标准格式

AI 大模型训练前,数据长什么样?

99% 都是这种表格——每一行是一条样本,每一列是一个字段:
在这里插入图片描述

question,answer
你好,你好呀,很高兴见到你
再见,再见,下次聊
天气如何,今天晴,25 度

这就是 CSV 格式——用逗号分隔的表格,Python 用 csv 模块专门处理。

import csv

# ========== 写入 CSV ==========
# 【Scratch积木】新建二维列表 [dataset],每行 [问题, 答案] 两列

data = [
    ["你好", "你好呀,很高兴见到你"],
    ["再见", "再见,下次聊"],
    ["天气如何", "今天晴,25 度"],
]

with open("ai_dataset.csv", "w", encoding="utf-8", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["question", "answer"])   # 写表头
    writer.writerows(data)                     # 一次写多行

# ========== 读取 CSV ==========
# 【Scratch积木】遍历二维列表,读出每一行

with open("ai_dataset.csv", "r", encoding="utf-8") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

运行结果

['question', 'answer']
['你好', '你好呀,很高兴见到你']
['再见', '再见,下次聊']
['天气如何', '今天晴,25 度']

核心易错说明

  • CSV 一定要 newline=""——不写的话每一行之间会多一个空行,这是 Windows/Mac 换行符差异导致的坑。
  • CSV 里的字段本身不能包含逗号,如果一定要有,Python 会自动加双引号包裹。

五、综合实战项目:AI 问答训练集管理工具

项目目标:做一个命令行工具,能录入问答对、查看全部、搜索、清空——关闭程序后数据不丢,下次打开还在。这就是所有 AI 训练平台后台的最小内核。

功能清单

  1. 启动时自动加载已有训练集
  2. 菜单驱动:添加 / 查看 / 搜索 / 清空 / 退出
  3. 每次修改自动落盘(CSV 格式)

Scratch 积木流程

  1. 定义定义列表聊天记录:从将问题或文字加入列表
    在这里插入图片描述

  2. 设置条数为聊天记录数,并播报连接保存信息
    在这里插入图片描述

  3. 输出聊天内容
    在这里插入图片描述

  4. 主循环:显示菜单 → 根据选择调用对应积木
    本章无 Scratch 对照。Scratch 不涉及文件系统操作,程序数据存在浏览器内存中,关闭即丢失。Python 的文件读写是真正的持久化能力,这是从"玩具程序"到"实用工具"的分水岭。
    在这里插入图片描述

Python 完整可运行源码

import csv
import os

DATASET_FILE = "ai_qa_dataset.csv"


# 【Scratch积木】定义积木 load_dataset() → 返回列表
def load_dataset():
    """从文件读取训练集,返回二维列表"""
    if not os.path.exists(DATASET_FILE):
        return []
    with open(DATASET_FILE, "r", encoding="utf-8") as f:
        reader = csv.reader(f)
        next(reader, None)   # 跳过表头
        return list(reader)


# 【Scratch积木】定义积木 save_dataset(dataset)
def save_dataset(dataset):
    """把训练集写入文件(覆盖式)"""
    with open(DATASET_FILE, "w", encoding="utf-8", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["question", "answer"])
        writer.writerows(dataset)


# 【Scratch积木】定义积木 add_qa()
def add_qa(dataset):
    """添加一条问答对"""
    q = input("请输入问题:").strip()
    a = input("请输入答案:").strip()
    if not q or not a:
        print("⚠️ 问题和答案都不能为空")
        return
    dataset.append([q, a])
    save_dataset(dataset)
    print(f"✅ 已保存,当前共 {len(dataset)} 条")


# 【Scratch积木】定义积木 show_all(dataset)
def show_all(dataset):
    """查看全部问答对"""
    if not dataset:
        print("📭 训练集为空")
        return
    print("=" * 40)
    for i, (q, a) in enumerate(dataset, 1):
        print(f"{i}. Q: {q}")
        print(f"   A: {a}")
    print("=" * 40)


# 【Scratch积木】定义积木 search_qa(dataset, keyword)
def search_qa(dataset):
    """按关键词搜索"""
    keyword = input("请输入搜索关键词:").strip()
    hits = [row for row in dataset if keyword in row[0] or keyword in row[1]]
    if not hits:
        print(f"🔍 未找到含「{keyword}」的问答对")
        return
    print(f"🔍 找到 {len(hits)} 条:")
    for i, (q, a) in enumerate(hits, 1):
        print(f"{i}. Q: {q}")
        print(f"   A: {a}")


# 【Scratch积木】主循环:重复执行直到 <选择 = "0">
def main():
    dataset = load_dataset()
    print("=" * 40)
    print("  🤖 AI 问答训练集管理工具")
    print(f"  已加载训练集,共 {len(dataset)} 条")
    print("=" * 40)

    while True:
        print("\n请选择操作:")
        print("  1. 添加问答对")
        print("  2. 查看全部")
        print("  3. 搜索问答")
        print("  4. 清空训练集")
        print("  0. 退出")

        choice = input("👉 ").strip()
        if choice == "1":
            add_qa(dataset)
        elif choice == "2":
            show_all(dataset)
        elif choice == "3":
            search_qa(dataset)
        elif choice == "4":
            confirm = input("⚠️ 确定清空吗?(y/n): ")
            if confirm.lower() == "y":
                dataset.clear()
                save_dataset(dataset)
                print("🗑️ 已清空")
        elif choice == "0":
            print(f"\n👋 再见!本次训练集共 {len(dataset)} 条问答对,已自动保存")
            break
        else:
            print("❓ 无效选择,请重新输入")


if __name__ == "__main__":
    main()

运行方式

  1. 保存为 day11_code.py
  2. 命令行 python day11_code.py
  3. 按菜单操作,退出后再打开程序,数据依然在

💡 这个工具的价值:你手上就有一个能自己积累训练数据的 AI 语料库。往后接入大模型 API(Day 19 会讲),把这些问答对喂给 AI 做微调或 RAG 检索,就是你自己的专属 AI 助手雏形

六、高频易错点总结

1. 忘写 encoding="utf-8" 中文乱码

Windows 默认 GBK,Mac/Linux 默认 UTF-8——不显式指定,跨平台就翻车。牢记一句:只要有中文,必写 encoding="utf-8"

2. w 模式会直接覆盖原文件

新手最容易踩:本来想追加,结果用了 w,一开原来一整个文件的内容瞬间清空追加永远用 a

3. 忘用 with 语句,忘调 close()

不用 with,就要手动 f.close()——一忘就可能出现数据没写完就没了、文件被锁定等诡异问题。开局无脑上 with

4. 写 CSV 忘写 newline=""

不写这个参数,Windows 上写出来的 CSV 每行之间都会多一个空行。这是 Python 官方文档专门强调过的坑。

5. 直接读不存在的文件

FileNotFoundError 直接崩程序——大项目里都要用 os.path.exists() 先判断,或用 try/except(预告 Day 13 会讲)。

八、💡 学习提示

为鼓励大家动手练习、吃透编程逻辑,本篇不提供 Scratch 成品源码,请对照截图亲手搭建积木完成复刻!

配套 Python 代码可直接复制运行学习。

九、往期历史笔记

  1. 编程启蒙|Scratch 转 Python 系列第 1 天:变量、数字运算积木双向对照(AI 基础数值计算实战)

  2. 编程启蒙|Scratch 转 Python 系列第 2 天:分支判断 if 积木双向对照(AI 指令条件过滤实战)

  3. 编程启蒙|Scratch 转 Python 系列第 3 天:循环重复积木双向对照(AI 批量循环处理,已收录 AI Agent 技术社区)

  4. 编程启蒙|Scratch 转 Python 系列第 4 天:字符串文本积木双向对照(AI 提示词拼接、文本清洗实战)

  5. 编程启蒙|Scratch 转 Python 系列第 5 天:自定义积木 / 函数双向对照(AI 工具封装、重复指令简化实战)

  6. 编程启蒙|Scratch 转 Python 系列第 6 天:列表、数组积木双向对照(AI 底层语法 / AI 批量数据处理实战)

  7. 编程启蒙|Scratch 转 Python 系列第 7 天:猜数字大挑战·升级版实战(AI 出题 + 二分查找最优解 + 完整命令行游戏)

  8. 编程启蒙|Scratch 转 Python 系列第8天:节奏敲击机游戏实战(AI节奏谱生成实战)

  9. 编程启蒙|Scratch 转 Python 系列第9天:字典/哈希表积木双向对照(AI大模型参数配置表实战)

  10. 编程启蒙|Scratch 转 Python 系列第 10 天:问答闯关游戏实战(AI 题库管理 + 多关卡剧本)

十、下一章预告

Scratch 转 Python 系列第 12 天:记忆翻牌游戏实战(游戏存档与排行榜持久化)

串联本篇文件读写 + Day 6 列表 + Day 9 字典,让翻牌游戏的成绩、最高纪录、玩家昵称全部存进文件——关掉游戏再打开,排行榜还在

十一、原创声明

本文为**「梅雅达编程笔记」**原创启蒙教程,禁止私自搬运、二次转载,转发请标注来源。

想要系统学完整套 Scratch 转 Python 课程,欢迎订阅下方专栏持续追更。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐