收官实战:把前九章的零件(容器、文件、异常、生成器、装饰器、argparse)组装成 Python 的看家本领——数据处理与自动化脚本。三个完整项目:CSV 数据处理(标准库)、网络请求与 API(第三方库的第一课)、以及一个带命令行界面的成品工具。学完这章,「Python 能帮我干什么」就有具体答案了。
任务:一份 students.csv(姓名,科目,分数),统计每人平均分并输出排名。CSV 模块处理「带逗号的字段」这类手写 split 会翻车的格式:
import csv
from collections import defaultdict
from pathlib import Path
def load_scores(path: Path) -> dict[str, list[float]]:
scores = defaultdict(list) # 第 4 章惯用法
with open(path, encoding="utf-8") as f: # 第 5 章 with + utf-8
for row in csv.DictReader(f): # 每行是字典!
scores[row["name"]].append(float(row["score"]))
return scores
def rank(scores: dict[str, list[float]]) -> list[tuple]:
avg = {name: sum(v) / len(v) for name, v in scores.items()} # 推导式
return sorted(avg.items(), key=lambda kv: kv[1], reverse=True) # 第 8 章 key=
def report(rows, out="rank.txt"):
with open(out, "w", encoding="utf-8") as f:
f.write("名次 姓名 平均分\n")
for i, (name, avg) in enumerate(rows, 1):
f.write(f"{i:>3} {name:<8} {avg:.1f}\n")
if __name__ == "__main__":
report(rank(load_scores(Path("students.csv"))))
函数分工(load/rank/report 各管一段)+ 类型注解 + pathlib + defaultdict + 推导式,前九章的所有招式在同一屏会师。Excel 文件换 openpyxl(pip 装),API 同款;大数据量把列表换成生成器管道(第 8 章),GB 级文件内存恒定。
第三方库的第一课。requests(事实标准,pip install requests)把 HTTP 变成人话(对比 Java 课第 13 章的 Socket 和第 9 章 API):
import requests
# GET:查(以公共 API 为例)
r = requests.get("https://api.github.com/repos/Sherfiter/Wiki", timeout=10)
r.raise_for_status() # 非 2xx 直接抛异常(第 5 章)
data = r.json() # JSON → 字典(第 7 章 json 模块)
print(data["stargazers_count"], data["language"])
# 带参数与请求头
r = requests.get("https://api.github.com/search/repositories",
params={"q": "python", "sort": "stars"},
headers={"User-Agent": "learn-python"},
timeout=10)
# POST:提交 JSON
r = requests.post(url, json={"name": "test"}, timeout=10)
纪律三条:timeout 必设(默认无超时 = 挂死风险)、raise_for_status 必调(404 不抛异常,不查就静默拿错误页)、密钥不进代码(环境变量 os.environ 或 .env,软件工程课第 9 章)。JSON 的读写(json.load/dump 与文件结合)是「配置文件」的 Python 惯用形态。
爬虫一瞥:requests + 正则/BeautifulSoup 抓静态页面即可起步;纪律:看 robots.txt、限速(time.sleep)、只抓公开数据。动态渲染页面(第 8 章 Web 前端的 SPA)要上 Selenium/Playwright(驱动真浏览器),那是另一门课的入口。
把 10.1 升级成正式工具:命令行参数、退出码、错误处理、日志:
import argparse, sys, logging
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")
def main(argv=None) -> int: # 返回退出码(0 成功)
parser = argparse.ArgumentParser(description="学生成绩排名工具")
parser.add_argument("csv", help="输入 CSV 文件")
parser.add_argument("-o", "--out", default="rank.txt", help="输出文件")
parser.add_argument("-n", "--top", type=int, default=10, help="只显示前 N 名")
args = parser.parse_args(argv)
try:
rows = rank(load_scores(Path(args.csv)))[: args.top]
except FileNotFoundError:
logging.error("文件不存在:%s", args.csv)
return 1 # 非 0 = 失败,shell 可感知
report(rows, args.out)
logging.info("已生成 %s", args.out)
return 0
if __name__ == "__main__":
sys.exit(main())
$ python ranker.py students.csv -o out.txt -n 5
INFO 已生成 out.txt
$ python ranker.py nofile.csv ; echo $?
ERROR 文件不存在:nofile.csv
1
argparse 自动生成 --help、自动校验类型;sys.exit 的退出码让脚本可以被 shell 组合(unix 管道思想)。「工具感」的三件套:argparse(参数)+ logging(替代 print,可分级可关)+ 返回码(可组合)——这是脚本与玩具的分界线(软件工程课第 10 章「可观测性」的个人版)。
按方向选下一站,每条都站在本课地基上: