课程 25:项目实战

学习目标

1. 选题与范围(MVP)

1.1 选题建议

选择与生活/学习相关且可在2-3周内完成的主题:数据采集与分析、API聚合助手、学习管理工具、小游戏/可视化等。

1.2 明确MVP与边界

SMART目标:具体、可衡量、可达成、相关性强、有时间限制。优先实现核心路径,延后“可有可无”的功能。

2. 需求分析与设计

2.1 用户故事与用例

# 用户故事示例
# 作为一名学生,我希望输入课程分数,自动计算绩点并导出报告。

2.2 数据模型草案

from dataclasses import dataclass
from typing import List, Dict

@dataclass
class Course:
    name: str
    score: float

@dataclass
class Student:
    id: int
    name: str
    courses: List[Course]

    def gpa(self) -> float:
        if not self.courses:
            return 0.0
        return sum(c.score for c in self.courses) / len(self.courses)

3. 技术选型与架构

3.1 路线选择

CLI工具(argparse)/ 桌面小工具 / Web最小应用(Flask)/ 数据分析脚本(pandas+matplotlib)。按目标与时间选择最简路线。

3.2 目录结构(建议)

project/
  app/              # 业务代码
    __init__.py
    models.py
    services.py
    cli.py          # 命令行入口
  tests/            # 单元测试
    test_models.py
  data/             # 输入输出数据
  requirements.txt
  README.md

4. 代码组织与工程化

4.1 命令行脚手架(argparse)

import argparse

def build_parser() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(prog="grader", description="成绩工具")
    sub = p.add_subparsers(dest="cmd", required=True)

    p_load = sub.add_parser("load")
    p_load.add_argument("file", help="CSV文件")

    p_report = sub.add_parser("report")
    p_report.add_argument("--top", type=int, default=10)
    return p

if __name__ == "__main__":
    args = build_parser().parse_args()
    if args.cmd == "load":
        print("loading", args.file)
    elif args.cmd == "report":
        print("top", args.top)

4.2 配置、日志与依赖

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
logger = logging.getLogger("project")
logger.info("启动应用")

5. 数据获取与存储

5.1 CSV/JSON 读写

import csv, json

# CSV读取
with open("data/students.csv", newline="", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    rows = list(reader)

# JSON写入
with open("data/report.json", "w", encoding="utf-8") as f:
    json.dump(rows, f, ensure_ascii=False, indent=2)

5.2 SQLite(可选)

import sqlite3
conn = sqlite3.connect("data/app.db")
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS student(id INTEGER PRIMARY KEY, name TEXT)")
cur.execute("INSERT INTO student(name) VALUES (?)", ("Alice",))
conn.commit()

6. API 集成与网络

6.1 requests + Session + 重试

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
session.headers.update({"User-Agent": "CS106A/1.0"})
retry = Retry(total=3, backoff_factor=0.5, status_forcelist=[429, 500, 502, 503, 504])
session.mount("https://", HTTPAdapter(max_retries=retry))

resp = session.get("https://httpbin.org/json", timeout=5)
resp.raise_for_status()
print(resp.json())

7. 面向对象设计(OOP)

7.1 服务与仓库分层

class StudentRepository:
    def __init__(self):
        self._students = {}

    def add(self, student):
        self._students[student.id] = student

    def get(self, student_id):
        return self._students.get(student_id)

class ReportService:
    def __init__(self, repo: StudentRepository):
        self.repo = repo

    def topk(self, k: int):
        data = list(self.repo._students.values())
        return sorted(data, key=lambda s: s.gpa(), reverse=True)[:k]

8. 测试与质量

8.1 unittest 基本示例

import unittest

class TestGpa(unittest.TestCase):
    def test_empty(self):
        s = Student(id=1, name="A", courses=[])
        self.assertEqual(s.gpa(), 0.0)

# if __name__ == "__main__":
#     unittest.main()

9. 错误处理与日志

9.1 防御式编程

def safe_div(x: float, y: float) -> float:
    if y == 0:
        raise ValueError("denominator must not be zero")
    return x / y

try:
    safe_div(1, 0)
except ValueError as e:
    logger.error("invalid input: %s", e)

10. 性能与并发(I/O密集)

10.1 线程池批量请求

from concurrent.futures import ThreadPoolExecutor, as_completed
import requests

urls = ["https://httpbin.org/delay/1" for _ in range(5)]

def fetch(u):
    return requests.get(u, timeout=3).status_code

with ThreadPoolExecutor(max_workers=5) as ex:
    futures = [ex.submit(fetch, u) for u in urls]
    print([f.result() for f in as_completed(futures)])

11. 演示与部署

11.1 CLI使用示例

# 约定README中给出调用示例
# python -m app.cli load data/students.csv
# python -m app.cli report --top 10

11.2 最小Web演示(可选:Flask)

from flask import Flask
app = Flask(__name__)

@app.route("/")
def index():
    return "Hello, Project!"

# if __name__ == "__main__":
#     app.run(debug=True)

12. 实际应用案例

12.1 学生成绩仪表盘

# 读取CSV -> 计算统计 -> 生成JSON -> 前端/命令行展示

12.2 API聚合助手

# 天气 + 翻译 + 汇率:统一封装 + 缓存 + 限流与重试

13. 常见错误与调试

14. 编程练习与挑战

练习1:基础

练习2:进阶

练习3:实战项目

15. 综合作业与项目

作业25:项目实战(提交物与评分要点)

任务1:需求与设计

提交包含用户故事、用例、数据模型与模块划分的设计文档(1-2页)。

任务2:实现与测试

实现核心功能,包含至少3个单元测试与日志;README给出运行与演示说明。

任务3:演示与复盘

录制1-3分钟演示视频或现场演示,附问题与改进清单。

学习建议:优先做出可运行的MVP;用小步提交与代码评审;为关键路径写测试;记录问题与复盘。
思考题:
拓展阅读: