码上问学问代码之理,学编程之道

第 4 章 · 数据处理

会了语法,就能“算”;会了数据处理,才能“用真实世界的数据算”。这一章我们从读写文件出发,学会处理 CSV / JSON,认识标准库,再借 pandas 体会“一行搞定一张表”的威力,最后用学生成绩分析把知识串成实战。

文件读写:让程序“记住”东西

with open 的安全读写:文件用完自动关,就算中途报错也照关with open(...) as f:① 打开文件拿 f② 缩进块里读写③ 块结束(或出错)自动 close老写法 open() + close()f = open(...); ...; f.close()✗ 中途报错 → close 执行不到✗ 文件句柄泄漏,写得多了系统报错模式速记:'r' 读 · 'w' 写(清空重来)· 'a' 追加 · encoding='utf-8' 处理中文必带'w' 的破坏力:文件原有内容直接清空——想保留旧内容用 'a'
先讲为什么程序运行时的变量,关掉电脑就没了。要把数据“留下来”或“读进来”,就得和文件打交道。几乎每个真实项目都要读写文件。
io.pypython可运行
# 写文件(没有会自动创建)
with open("note.txt", "w", encoding="utf-8") as f:
    f.write("码上问学\n")
    f.write("Python 第4章\n")

# 读文件
with open("note.txt", "r", encoding="utf-8") as f:
    content = f.read()
print(content)
运行输出Python 运行结果
运行结果: 码上问学 Python 第4章
讲透
  • with open(...) as f 是“优雅打开文件”的标准写法:离开代码块时自动关闭,绝不会漏关。
  • "w" 写、"r" 读、"a" 追加;encoding="utf-8" 务必写明,否则中文易乱码。
  • f.read() 一次读全部;大文件用 f.readlines() 或逐行 for line in f。
避坑
  • 用 "w" 模式打开文件会清空原文件;想保留旧内容、只往末尾加,请用 "a" 追加。
  • f.write() 只能接收字符串,写数字前要先 str(...),否则报 TypeError。
shopping.pypython可运行
# 生活化:把购物清单“存”下来,再读出来
with open("shopping.txt", "w", encoding="utf-8") as f:
    for item in ["牛奶", "鸡蛋", "面包"]:
        f.write(item + "\n")

with open("shopping.txt", "r", encoding="utf-8") as f:
    items = f.read().splitlines()
print("要买:", items)
print("共", len(items), "件")
运行输出Python 运行结果
运行结果: 要买: ['牛奶', '鸡蛋', '面包'] 共 3 件
read_lines.pypython可运行
# 实战片段:逐行读取并标注行号(处理日志/数据文件常用)
with open("notes.txt", "w", encoding="utf-8") as f:
    f.write("买菜\n取快递\n写作业\n")

with open("notes.txt", "r", encoding="utf-8") as f:
    lines = f.readlines()

for i, line in enumerate(lines, 1):
    print(f"{i}. {line.strip()}")
运行输出Python 运行结果
运行结果: 1. 买菜 2. 取快递 3. 写作业

CSV 与 JSON:两种最常见的数据格式

CSV 与 JSON:一个给 Excel 看,一个给程序看CSV:逗号分隔的表格title,author,views静夜思,李白,328春望,杜甫,201✓ Excel 能直接打开 · 行列结构简单JSON:程序通用的数据格式{"title": "静夜思", "author": "李白", "views": 328}✓ 能表达嵌套(列表套字典)· 接口标配Python 里:csv 模块读表格,json.loads() 把文本变字典,json.dumps() 变回文本
csv_json.pypython可运行
import csv, json

# CSV:表格数据(逗号分隔)
rows = [["姓名", "分数"], ["张三", 90], ["李四", 85]]
with open("scores.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.writer(f)
    w.writerows(rows)

# JSON:结构化的“键值”数据,前后端通用
data = {"class": "一班", "avg": 87.5, "top": ["张三", "李四"]}
with open("info.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

# 读回来
with open("info.json", "r", encoding="utf-8") as f:
    back = json.load(f)
print(back["top"])    # ['张三', '李四']
运行输出Python 运行结果
运行结果: ['张三', '李四']
讲透CSV 是“表格的纯文本版”,Excel 能直接打开;JSON 是“字典的字符串版”,是网络接口的通用语言。学 Python 数据处理,本质上就是学会在这两种格式和 Python 对象之间来回转换。
避坑
  • 用 csv.writer 写文件时,务必带上 newline=""(尤其 Windows),否则每行之间会被多塞一个空行。
  • json.dump/dumps 默认 ensure_ascii=True,中文会变成 \u4e2d... 转义;要保留中文就写 ensure_ascii=False。
ledger.pypython可运行
import csv

# 生活化:记一笔每天的开支,再统计总支出
records = [
    ["日期", "项目", "金额"],
    ["10-01", "早餐", 12],
    ["10-01", "公交", 4],
    ["10-02", "午餐", 25],
]
with open("ledger.csv", "w", newline="", encoding="utf-8") as f:
    csv.writer(f).writerows(records)

total = 0
with open("ledger.csv", "r", encoding="utf-8") as f:
    for row in csv.reader(f):
        if row[0] != "日期":
            total += int(row[2])
print("总支出:", total, "元")
运行输出Python 运行结果
运行结果: 总支出: 41 元
config.pypython可运行
import json

# 实战片段:把程序设置存成 JSON,再读回来
config = {"主题": "深色", "字体": 14, "自动保存": True}
text = json.dumps(config, ensure_ascii=False)
print("保存为:", text)

back = json.loads(text)
print("读取主题:", back["主题"])
运行输出Python 运行结果
运行结果: 保存为: {"主题": "深色", "字体": 14, "自动保存": true} 读取主题: 深色

标准库:装好 Python 就自带的“工具箱”

stdlib.pypython可运行
import os, datetime

print(os.getcwd())                 # 当前目录
print(os.listdir("."))             # 当前目录下的文件列表
print(datetime.date.today())       # 今天日期
print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M"))  # 格式化时间
运行输出Python 运行结果
运行结果: /Users/you/project ['note.txt', 'scores.csv', 'info.json'] 2026-10-10 2026-10-10 12:50
讲透os 管“操作系统相关”(路径、目录、文件),datetime 管“时间”。标准库的意思是“不用安装、直接 import”,遇到常见需求先想“标准库有没有”,能省下大量造轮子的时间。
避坑别把自己的脚本命名为 random.py、json.py、csv.py 这类名字——它会“遮住”同名的官方标准库模块,导致你 import 时拿到的是自己那个空文件,程序直接报错。
pizza.pypython可运行
import math

# 生活化:算一块圆形披萨的面积
r = 8
area = math.pi * r ** 2
print(f"半径 {r} 的披萨面积:{area:.1f}")
运行输出Python 运行结果
运行结果: 半径 8 的披萨面积:201.1
vote.pypython可运行
from collections import Counter

# 实战片段:统计一次投票结果(贴近项目里的“计数统计”)
votes = ["Python", "Java", "Python", "C++", "Python", "Java"]
result = Counter(votes)
print("各语言得票:", dict(result))
print("第一名:", result.most_common(1))
运行输出Python 运行结果
运行结果: 各语言得票: {'Python': 3, 'Java': 2, 'C++': 1} 第一名: [('Python', 3)]

pandas 简介:一行搞定一整张表

先讲为什么用基础语法处理一张几千行的表,要写很多循环。pandas 把“表”变成对象,分组、求平均、筛选都成一行操作——它是数据科学的基石。
pandas_demo.pypython需安装 pandas
import pandas as pd

df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五"],
    "数学": [90, 85, 77],
    "英语": [88, 92, 70],
})

print(df.mean(numeric_only=True))   # 每科平均分
print(df[df["数学"] > 80])          # 筛出数学 > 80 的行
运行输出Python 运行结果
运行结果: 数学 84.0 英语 83.333333 dtype: float64 姓名 数学 英语 0 张三 90 88 1 李四 85 92
讲透df 是一张“带列名的数据表”。df["数学"] > 80 会一次性对整个“数学”列做判断,返回一个布尔序列,再拿它去筛行——这种“向量化”思维,正是 pandas 又快又简洁的核心。安装:pip install pandas。
避坑
  • pandas 不是自带模块,首次使用要先 pip install pandas;装好前直接 import pandas 会报 ModuleNotFoundError。
  • 取某一列要用 df["列名"](中括号),写成 df.列名 当列名含中文或空格时会失效。
steps.pypython可运行
# 生活化:记录一周步数,看看平均和达标天数
# (pandas 里一行:steps["步数"].mean() / steps[steps["步数"]>=10000])
data = [
    ("一", 8000), ("二", 12000), ("三", 6000),
    ("四", 11000), ("五", 9000),
]
vals = [v for _, v in data]
print("平均每天:", sum(vals) // len(vals))
达标 = [d for d in data if d[1] >= 10000]
print("达标天数:", len(达标))
运行输出Python 运行结果
运行结果: 平均每天: 9200 达标天数: 2
score_stat.pypython可运行
import csv

# 实战片段:读一份成绩 CSV 并做“一行 pandas”的等效统计
# (pandas 等效:df = pd.read_csv("score.csv"); print(df.describe()))
rows = []
with open("score.csv", "w", newline="", encoding="utf-8") as f:
    f.write("姓名,数学,英语\n张三,90,88\n李四,85,92\n王五,77,70\n")

with open("score.csv", "r", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        rows.append((row["姓名"], int(row["数学"]), int(row["英语"])))

math_scores = [r[1] for r in rows]
eng_scores = [r[2] for r in rows]
print("数学:共", len(math_scores), "人 平均",
      round(sum(math_scores)/len(math_scores), 1))
print("英语:共", len(eng_scores), "人 平均",
      round(sum(eng_scores)/len(eng_scores), 1))
print("最高数学:", max(rows, key=lambda r: r[1])[0])
运行输出Python 运行结果
运行结果: 数学:共 3 人 平均 84.0 英语:共 3 人 平均 83.3 最高数学: 张三

实战:学生成绩分析(纯标准库版)

综合 文件 / CSV / 循环 / 字典

不依赖 pandas,用基础语法也能做分析:读入一份 CSV,算出每科平均分与全班总分最高的学生。这正是第 7 章“成绩可视化报告”的前身。

analyze.pypython可运行
import csv

scores = []
with open("scores.csv", "r", encoding="utf-8") as f:
    for row in csv.DictReader(f):      # 每行自动变成 {列名: 值}
        row["数学"] = int(row["数学"])
        row["英语"] = int(row["英语"])
        scores.append(row)

# 每科平均分
for subj in ["数学", "英语"]:
    avg = sum(s[subj] for s in scores) / len(scores)
    print(f"{subj} 平均分:{avg:.1f}")

# 总分最高
top = max(scores, key=lambda s: s["数学"] + s["英语"])
print("总分最高:", top["姓名"])
运行输出Python 运行结果
运行结果: 数学 平均分:84.0 英语 平均分:83.3 总分最高: 张三
讲透csv.DictReader 把表头当键、每行当字典,这正是第 2 章“字典”的真正用武之地。max(..., key=lambda s: ...) 用“总分”作为比较依据——把第 3 章的 lambda 用在了真实排序上。

第 4 章小结

动手练习

1写一个程序:把下面三位同学的成绩写入一个 CSV 文件(含表头“姓名,数学,英语”),再读回来,打印数学平均分和英语最高分的同学姓名。数据:小明 88/95、小红 92/80、小刚 75/88。

提示:写入用 csv.writer(...).writerows(...);读回用 csv.DictReader 把每行变成字典,再用 max(..., key=lambda r: ...) 找最高分。

practice1.pypython可运行
import csv

# 参考答案:写入并分析成绩 CSV
data = [
    ["姓名", "数学", "英语"],
    ["小明", 88, 95],
    ["小红", 92, 80],
    ["小刚", 75, 88],
]
with open("grades.csv", "w", newline="", encoding="utf-8") as f:
    csv.writer(f).writerows(data)

rows = []
with open("grades.csv", "r", encoding="utf-8") as f:
    for r in csv.DictReader(f):
        rows.append((r["姓名"], int(r["数学"]), int(r["英语"])))

math_avg = sum(r[1] for r in rows) / len(rows)
top_eng = max(rows, key=lambda r: r[2])
print(f"数学平均分:{math_avg:.1f}")
print("英语最高分:", top_eng[0])
运行输出Python 运行结果
运行结果: 数学平均分:85.0 英语最高分: 小明