ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

【FDE系列】阶段2:Day 30:生产代码规范 — 测试、类型注解、配置管理(第二周收官)

【FDE系列】阶段2:Day 30:生产代码规范 — 测试、类型注解、配置管理(第二周收官) 前言FDE系列内容总纲【大纲】FDE 前沿部署工程师学习系列教程-CSDN博客前置课程列表阶段一【FDE系列】阶段1Day 1AI 层级关系 — 四个嵌套的圈-CSDN博客【FDE系列】阶段1Day 2AI 三阶段发展史 — 会认 → 会判断 → 会创造-CSDN博客【FDE系列】阶段1Day 3符号 AI vs 机器学习 — 两条路线的本质区别-CSDN博客【FDE系列】阶段1Day 4Transformer 的历史意义 — 2017 年的分水岭-CSDN博客【FDE系列】阶段1Day 5本周复习与自测 — 检验你的 AI 认知地基-CSDN博客【FDE系列】阶段1Day 6Transformer 架构 — 一张图纸盖出千千万万栋楼-CSDN博客【FDE系列】阶段1Day 7LLM 本质 — 文字接龙机器-CSDN博客【FDE系列】阶段1Day 8Token — 模型眼中的最小单位-CSDN博客【FDE系列】阶段1Day 9AI 幻觉 — 为什么会一本正经地胡说八道-CSDN博客【FDE系列】阶段1Day 10上下文窗口 — 模型的记忆力上限 本周复习-CSDN博客【FDE系列】阶段1Day 11Prompt — 给模型立规矩-CSDN博客【FDE系列】阶段1Day 12Memory — 让模型记住上下文【FDE系列】阶段1Day 13RAG — 给模型配图书管理员-CSDN博客【FDE系列】阶段1Day 14Tool Use — 让模型动手操作-CSDN博客【FDE系列】阶段1Day 15MCP — 统一的工具接口标准 第三周复习-CSDN博客【FDE系列】阶段1Day 16什么是 FDE — 把 AI 变成客户结果的人-CSDN博客【FDE系列】阶段1Day 17FDE vs 传统实施 — 三大本质区别-CSDN博客【FDE系列】阶段1Day 18FDE 三重身份 C6 胜任力模型-CSDN博客【FDE系列】阶段1Day 19七阶段行动路径 行业经验的价值-CSDN博客【FDE系列】阶段1Day 20阶段总结与产出物 — 第一阶段收官-CSDN博客阶段二【FDE系列】阶段2Day 21Python 环境搭建 — 写出你的第一行代码-CSDN博客【FDE系列】阶段2Day 22变量、数据类型、条件判断 — Python 的“记忆“和“判断“-CSDN博客【FDE系列】阶段2Day 23循环与函数 — 让代码跑 100 遍、把逻辑打包复用-CSDN博客【FDE系列】阶段2Day 24数据结构 — 列表、字典、集合、元组-CSDN博客【FDE系列】阶段2Day 25文件读写与 JSON — 让程序连通外部数据第一周收官-CSDN博客【FDE系列】阶段2Day 26模块化编程 — 把代码拆成“抽屉柜“-CSDN博客【FDE系列】阶段2Day 27异常处理与日志 — 让程序“摔不烂、查得到“-CSDN博客【FDE系列】阶段2Day 28FastAPI 入门 — 把你的函数变成 API 服务-CSDN博客【FDE系列】阶段2Day 29FastAPI 进阶 — Pydantic 模型与完整 CRUD 实战-CSDN博客阶段2·Day 30生产代码规范 — 测试、类型注解、配置管理第二周收官FDE 学习系列教程 · 第二阶段 · 第 2 周 · Day 5预计时长3 小时 | 难度★★★☆☆ | 前置知识Day 26-29模块、异常、FastAPI、Pydantic一句话目标掌握单元测试pytest、类型注解、环境变量配置和依赖管理四件套让你的项目从我电脑上能跑变成换台电脑、交给别人也能跑。‍‍ 老哥开场白本周最后一天讲四样不写功能但决定项目能不能交付的东西。先想个画面你的工单 API 有 5 个接口、十几个判断分支。每次改代码你怎么确认没改坏全靠手动在/docs里点一遍点两天还行项目活三个月、改了五十次呢而且交付客户时还有一堆现实问题 客户服务器上怎么一键装齐你用的 12 个第三方库 API 密钥、数据库地址能硬编码在代码里吗不能交付出去等于泄密 有些 bug 运行好几个月才触发一次怎么提前防今天四件武器类型注解— 让编辑器在你敲代码时就抓 bugpytest 测试— 让代码自动证明自己没坏.env 配置— 密码、地址等环境差异全部外置requirements.txt— 一份依赖清单环境一键复刻学完今天第二阶段的 Python 地基就正式浇筑完毕。️ 类型注解给编辑器递小抄类型注解 写在变量、函数参数、返回值后面的这个值是什么类型的说明。Python 用它做静态检查、IDE 提示、框架如 FastAPI、Pydantic的自动校验。语法变量名: 类型 值冒号后面就是类型注解。你其实已经见过它FastAPI 里的temperature: int、payload: TicketCreate。基本写法# 变量注解 device_name: str 注塑机A1 temperature: int 82 devices: list[str] [A1, A2] device: dict[str, object] {name: A1, temp: 82} # 函数注解参数类型 - 返回值类型 def grade_temperature(t: float) - str: if t 90: return 紧急 return 正常def 函数名(参数: 类型) - 返回类型: └─ 进门的东西是什么 └─ 吐出来的是什么注解不强制但极其有用注意Python运行时不会强制检查注解。你写t: int然后传字符串代码照样跑除非 FastAPI/Pydantic 帮你拦。那写它图什么1. 编辑器神补全VS Code 知道 t 是 floatt. 后面自动列出可用方法 2. 提前抓 bug t.upper()数字没有 upper立刻黄线警告 3. 即文档 不用读函数体看签名就知道该传什么、会得到什么 4. 框架底座 FastAPI/Pydantic 全靠注解工作 把光标悬停在 VS Code 里被注解的函数上签名直接弹出来调用处参数传错类型没运行就画黄线。注解是写给人和工具看的Python 解释器基本不看——但看的那些人里包括半年后的你自己。使用者作用IDEPyCharm/VSCode自动补全、错误提示静态检查工具mypy/pyright提前发现类型错误Pydantic运行时校验数据FastAPI根据注解解析请求、生成文档dataclasses自动生成__init__等Pydantic / FastAPI 是特例它们真的会在运行时读取并执行注解里的规则。常用注解速查基础类型x: int 1 y: float 1.0 s: str hi b: bool True n: None None容器类型Python 3.9 可直接用小写names: list[str] [a, b] scores: dict[str, int] {数学: 90} point: tuple[int, int] (1, 2) unique: set[int] {1, 2, 3}旧写法3.8 及以前需要用typing模块from typing import List, Dict, Tuple, Set names: List[str] [a]可选类型可能是这个类型也可能是 None# Python 3.10 nickname: str | None None # Python 3.9 及以前 from typing import Optional nickname: Optional[str] Nonestr | None读作str 或 None。联合类型多种可能value: int | str 42 # 3.10 from typing import Union value: Union[int, str] 42 # 旧写法任意类型from typing import Any data: Any whatever # 放弃类型检查建议新写的函数都加上参数和返回值注解。旧代码不强求。这是成本极低、回报极高的习惯。✅ pytest让代码自动验收代码pytest 是 Python 生态中最流行的测试框架它的设计哲学是“让写小测试变得容易同时能扩展到复杂的函数测试”。相比于 Python 标准库自带的unittestpytest 用更简洁的语法实现了更强大的功能。为什么必须有测试没有测试时改代码的心理活动是这样的我就改个阈值……应该没影响别处吧 这个函数三处都在用……改完我点哪个接口来着 上线两周后报 bug……咦是不是我上次改的有测试时改代码 → 敲一行命令 → 3 秒内 50 个检查点自动跑完 → 全绿放心红了当场知道哪坏了安装与第一个测试pip install pytest安装后可以用pytest --version验证。规矩测试代码放在test_开头的文件里测试函数也以test_开头。新建test_grading.py和昨天的grading.py放一起作用是对每个临界点和临界点的相邻值测试from grading import grade_temperature def test_紧急阈值(): assert grade_temperature(95) 紧急 assert grade_temperature(90) 紧急 # 边界值90 也算 def test_严重区间(): assert grade_temperature(89.9) 严重 assert grade_temperature(80) 严重 def test_正常区间(): assert grade_temperature(60) 正常 assert grade_temperature(0) 正常assert断言是 Python 中的一个调试辅助语句用于在代码中插入检查点确保某个条件为真。如果条件为假程序会抛出AssertionError异常并终止执行。基本语法assert condition # 或 assert condition, error_messagecondition一个返回布尔值的表达式error_message可选断言失败时显示的错误信息同一函数里第一个 assert 失败后后面的就不会执行了运行在命令行运行pytest它会自动发现当前目录及子目录下所有符合命名规则的测试文件并执行。pytest #或 pytest -v #-v 是 --verbose详细模式的简写让测试输出更详细。test_grading.py::test_紧急阈值 PASSED test_grading.py::test_严重区间 PASSED test_grading.py::test_正常区间 PASSED 3 passed in 0.02s pytest和pytest -v 命令执行对比 测试最该盯的是边界值阈值是 90就必须测 89.9、90、90.1 三个点。绝大多数分级 bug 都死在边界上——和的差别就是事故。故意看一次红灯把grading.py里的 90手滑改成 90再跑FAILED test_grading.py::test_紧急阈值 assert 严重 紧急 where 严重 grade_temperature(90)运行实际示例测试精准地告诉你90 这个点挂了。红 → 改 → 绿这个循环就是工程师的安全感来源。pytest 帮你检查异常传垃圾数据应该抛异常也能测import pytest from grading import grade_temperature def test_非法输入要报错(): with pytest.raises(TypeError): grade_temperature(高温) # Day 27 的防御性检查 with pytest.raises(ValueError): grade_temperature(999)测 APITestClientTestClient是 FastAPI以及其底层的 Starlette提供的测试客户端专门用来在测试中调用你的 Web 应用而不需要真的启动一个服务器比如uvicorn。它底层基于我们刚讨论过的httpx库。安装httpxpip install httpxhttpx是一个功能全面的 HTTP 客户端用于发送 HTTP 请求GET、POST 等。它的特点是API 与requests高度相似——会requests就能快速上手支持异步async/await——这是它相对 requests 最大的优势支持 HTTP/2——需要额外安装内置连接池、超时、重定向、Cookie 等# test_api.py from fastapi.testclient import TestClient from main import app client TestClient(app) def test_创建工单成功(): resp client.post(/tickets, json{ title: 测试工单, reporter: 测试员 }) assert resp.status_code 201 data resp.json() assert data[title] 测试工单 assert data[status] 待处理 # 默认值生效 def test_缺标题被拦(): resp client.post(/tickets, json{reporter: 测试员}) assert resp.status_code 422 def test_查不存在的工单(): resp client.get(/tickets/9999) assert resp.status_code 404这就是 Day 29 你在/docs里手动点的那套流程——现在它自动化了改完代码一秒回归。from main import appapp是被测的 FastAPI 实例定义在main.py里client TestClient(app)把应用包装成测试客户端之后就能用client.get/post发请求然后是三个测试函数分别测试成功、校验失败、资源不存在三种典型场景。这节会的同学直接往下翻。不过边界值必测异常路径也要测这两条原则十秒扫一眼记住不亏。 配置管理.env 与环境变量硬编码配置的灾难# ❌ 反面教材 API_KEY sk-xxxxxxxxxxxx # 密钥写在代码里 DB_HOST 192.168.1.50 # 客户内网地址写死三个致命问题1. 代码发给客户/传上 GitHub 密钥泄露 2. 开发环境、测试环境、客户环境地址不同改代码 3. 换个部署地点就要重新改源码重新打包正确做法配置与代码分离新建.env文件放项目根目录# .env —— 每个环境一份绝不提交给客户/代码仓库 APP_ENVdev DB_HOST192.168.1.50 DB_PORT3306 API_KEYsk-xxxxxxxxxxxx LOG_LEVELINFO读取它。标准库就能读环境变量import os api_key os.getenv(API_KEY) # 读不到返回 None db_host os.getenv(DB_HOST, localhost) # 读不到用默认值更省心的方式pydantic-settings和 FastAPI 同门pydantic-settings是一个基于 Pydantic 的配置管理库。简单说它让你能用类型安全的方式从环境变量、.env文件等来源读取应用配置。它原本是 Pydantic v1 内置的BaseSettings在 Pydantic v2 中被拆分成了独立的官方包所以现在需要单独安装。pip install pydantic-settings# config.py from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config SettingsConfigDict(env_file.env) app_env: str dev db_host: str localhost db_port: int 3306 api_key: str log_level: str INFO settings Settings() # 全局唯一配置对象BaseSettingsPydantic 提供的基类专门用于从环境变量 / .env 文件读取配置。SettingsConfigDict用于配置BaseSettings行为的字典类型Pydantic v2 引入替代 v1 的内部class Config。model_configPydantic v2 的配置入口。env_file.env告诉 Pydantic 自动加载项目根目录下的.env文件。app_env: str dev​​​​​​​类型自动转换比如db_port声明为int即使.env里写的是字符串3306也会自动转成整数。默认值兜底如果环境变量和.env都没有提供就用默认值。大小写不敏感环境变量DB_HOST、db_host、Db_Host都能匹配到db_host字段。settings Settings()在模块导入时立即执行读取环境变量和.env完成校验和类型转换。得到一个全局唯一的配置对象其他模块只需from config import settings即可复用。# 其他文件里用 from config import settings print(settings.db_host) # 自动从 .env 读取且类型已转好db_port 是 int工作原理 .env 里写 DB_HOST192.168.1.50 → settings.db_host 自动拿到 192.168.1.50 → 字段声明 int3306 自动转成 3306 → .env 里没写的用代码里的默认值必做.gitignore新建.gitignore下周 Git 模块会细讲今天先建# .gitignore —— 列在这里的文件不会被提交 .env venv/ __pycache__/ *.log 巡检报告.txt铁律任何密钥、密码、内网地址只能出现在 .env 里.env 永远不进代码仓库。可以给客户一个.env.example值留空的模板让对方照模板填自己的真实配置。 依赖管理requirements.txt它解决什么问题你在虚拟环境里 pip install 了 fastapi、uvicorn、pytest、httpx、pydantic-settings……客户怎么知道要装哪些、什么版本答案一份依赖清单。# 导出当前环境所有库及精确版本 pip freeze requirements.txt命令拆解pip freeze requirements.txt │ │ │ │ │ └─ 目标文件名约定俗成 │ └─ 重定向符把命令输出写入文件覆盖原内容 └─ pip 子命令列出已安装包及版本生成的文件fastapi0.115.0 uvicorn0.30.6 pydantic2.9.2 pydantic-settings2.5.2 pytest8.3.3 httpx0.27.2 ...客户拿到项目一条命令复刻整个环境python -m venv venv venv\Scripts\activate pip install -r requirements.txt两种风格pip freeze快照式 列出所有库含依赖的依赖 精确版本 优点版本完全锁定我这能跑你那也能跑 缺点文件长升级要整体处理 手写精简版声明式 只写你直接用的顶层库 fastapi0.115 uvicorn[standard]0.30 pytest 优点清晰小版本自动升级 建议学习项目用 freeze正式交付两者结合requirements.txt内容说明项目说明精确版本用锁定保证可复现包含传递依赖不仅有你直接装的还有它们依赖的库只列第三方包pip、setuptools等一般不在列视环境而定平台相关在 Windows 导出的可能含pywin32等换 Linux 会装不上新锐工具uv认识一下uv是 2024 年爆火的新一代 Python 包管理器Rust 写的比 pip 快 10-100 倍pip install uv uv venv # 建虚拟环境 uv add fastapi uvicorn # 加依赖自动管理清单和锁定文件 uv run python main.py # 在项目环境里运行 uv sync # 按清单一键同步环境 眼下继续用 pip venv 完全没问题客户现场也最通用。但 2026 年的新项目越来越多直接用 uv至少要听得懂、看得懂它的pyproject.toml。这块是专业中的专业不懂细节也不影响用先混个眼熟。️ FDE 实战给工单 API 补齐交付四件套以 Day 29 的工单项目为基础完成四步升级1. 加类型注解改 services 层# services.py def create_ticket(payload: dict) - dict: new_id max((t[id] for t in TICKETS), default0) 1 return {id: new_id, status: 待处理, description: , **payload} def find_ticket(ticket_id: int) - dict | None: for t in TICKETS: if t[id] ticket_id: return t return None2. 外置配置# .env APP_ENVdev LOG_LEVELDEBUG PAGE_SIZE_DEFAULT20# config.py from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config SettingsConfigDict(env_file.env) app_env: str prod log_level: str INFO page_size_default: int 50 settings Settings()3. 补一组测试# test_services.py from services import create_ticket, find_ticket def test_创建工单自动编号(): before create_ticket({title: T1, reporter: 甲}) after create_ticket({title: T2, reporter: 乙}) assert after[id] before[id] 1 assert after[status] 待处理 def test_查不到返回None而不是崩溃(): assert find_ticket(99999) is None4. 锁定依赖 验收pip freeze requirements.txt # 完整交付验收模拟拿到项目的新人 # 1. 删掉 venv 文件夹 # 2. python -m venv venv venv\Scripts\activate # 3. pip install -r requirements.txt # 4. 复制 .env.example 为 .env 并填写 # 5. pytest -v → 全绿 # 6. uvicorn main:app --reload → /docs 正常这套流程能跑通说明你的项目真正具备了可移植、可交接、可回归的交付属性。️ 第二周总复习知识地图工程化四板斧Day 26-30 │ ├─ 代码组织Day26 │ ├─ 模块 .py 文件包 文件夹 __init__.py │ ├─ import / from...import / as │ ├─ if __name__ __main__ 自测开关 │ ├─ 标准库datetime / pathlib / random / json │ └─ 职责分离一个文件一类事 │ ├─ 健壮性Day27 │ ├─ try / except精确捕获具体异常 │ ├─ else / finally / raise │ ├─ 批次容错坏数据跳过记录 │ ├─ logging 五级DEBUG/INFO/WARNING/ERROR/CRITICAL │ └─ exc_infoTrue 保留堆栈 │ ├─ 服务化Day28-29 │ ├─ HTTPGET/POST/PUT/DELETE 状态码 │ ├─ FastAPIapp 路由 uvicorn 启动 │ ├─ /docs 自动文档 │ ├─ 路径参数是谁vs 查询参数怎么筛 │ ├─ PydanticBaseModel/Field 校验契约 │ ├─ CRUD201 创建 / 404 不存在 / 422 参数错 │ ├─ exclude_unset 部分更新 │ └─ 分层接口层 / 业务层 / 数据层 │ └─ 可交付Day30 ├─ 类型注解参数 : 类型 - 返回类型 ├─ pytesttest_ 文件 assert TestClient ├─ 边界值必测、异常路径必测 ├─ .env pydantic-settings配置外置 ├─ .gitignore密钥永不入库 └─ requirements.txt / uv依赖复刻自测 10 题# 1. from x import y 和 import x 的使用区别 # 2. 模块里的自测代码为什么要放在 if __name__ __main__: 下 # 3. 裸 except: 为什么是恶习 # 4. 一条坏数据不能拖垮整批处理代码结构长什么样 # 5. logging 五个级别按严重程度排序 # 6. 浏览器访问 /devices/A1?fulltrue路径参数和查询参数分别是什么 # 7. Pydantic 的 Field(pattern^(高|中|低)$) 起什么作用 # 8. PUT 部分更新时为什么要用 model_dump(exclude_unsetTrue) # 9. .env 为什么必须写进 .gitignore # 10. 客户拿到项目后用哪条命令安装全部依赖第二周排错速查表现象原因处理ModuleNotFoundError模块没装/目录不对pip install检查运行目录和包结构改代码服务没反应没加--reloaduvicorn 启动加--reloadPOST 返回 422请求体不符合模型看 detail 里 loc 指出的字段接口返回 404路径不匹配/资源不存在检查路由拼写、路径顺序.env不生效没装 pydantic-settings/文件名错pip install确认文件名就是.envpytest 提示 no tests ran文件/函数没以test_开头改文件名和函数名端口 8000 被占用旧服务没关换端口--port 8001或结束旧进程 本课小结知识点一句话记住类型注解def f(x: int) - str给人和编辑器看pytesttest_文件 assertpytest -v跑边界值阈值点 ±0.1 必须测TestClient不起服务也能测 API.env环境差异和密钥的唯一住所.gitignore.env、venv、日志永不入库requirements.txtpip freeze 导出pip install -r安装uv新一代快 100 倍的包管理器认识即可今天最核心的认知能跑和能交付之间隔着四样东西——结构清晰模块、摔而不烂异常日志、行为可信测试、环境可复刻配置依赖。这四样不产生任何新功能但它们决定了客户敢不敢把系统放进真实车间。 课后练习测试补全给工单 API 再写 3 个测试——优先级非法返回 422、删除后再查返回 404、状态筛选?status已解决返回正确数量。跑pytest -v确保全绿。配置驱动给 API 加一个从配置读取的默认分页大小PAGE_SIZE_DEFAULT让GET /tickets支持limit参数不传时用配置值。在.env里改成 1验证默认行为变化。交付演练把项目打包思路在笔记里写成一份《新人上手指南》从拿到代码到看到/docs一共几条命令、需要手动填什么配置这份指南以后就是你交付文档的模板。 第二周收官两周 Python 你学到了哪十天前你刚装好 Python。盘点第二阶段 Python 模块大纲 2.1的全部要求✅ Week 1语法、数据结构、文件与 JSON——能写数据处理脚本✅ Week 2模块化、异常日志、FastAPI CRUD、测试与配置——能写可交付的 API 服务你现在手里有一个分层结构、带数据校验、有日志、有测试、配置外置、依赖可复刻的工单 API。它还很简陋数据在内存里但骨架是生产级的。第 3 周给它接上 SQL 数据库第 4-5 周用 Docker 打包部署第 6 周对接真实企业系统——一块块都是往这个骨架上挂肉。 本周反思十天里你觉得自己跨越最大的是哪一步哪一天的概念现在回头看已经理所当然了如果让你给同样零编程基础的同事讲明白什么是 API你会用什么类比试着讲 3 分钟。下周预告第 3 周SQL 与数据处理API 有了数据还躺在内存里——服务一重启工单全没。下周进入数据的世界SQL 增删改查、多表 JOIN、聚合统计再用 Python 直连数据库。客户最值钱的资产都在数据库里能读会查会清洗你才真正接得住 FDE 的核心战场。养足精神下周见FDE 学习系列教程 · 第二阶段 · 第 2 周 Day 5本周收官· 完
返回列表