Claude Code Python 入门实战:uv、pytest、Ruff 与 FastAPI
用Claude Code搭建可运行的Python项目:uv环境、pyproject、pytest、Ruff、FastAPI与安全迭代流程。
很多初学者第一次用 Claude Code 写 Python,并不是卡在“能不能生成代码”,而是卡在“这个项目到底算不算能运行”。虚拟环境不一致、测试找不到模块、格式化工具没有配置、API 能启动但没有验证命令,这些问题会让学习过程变得很混乱。
这篇文章用一个小任务管理项目说明更稳妥的做法:先用 uv 或标准库 venv 建环境,把依赖与工具配置放进 pyproject.toml,再写一个最小 FastAPI API 和可选 CLI,最后用 pytest 与 ruff 做验证。Claude Code 的价值不是一次生成大项目,而是让每次修改都有清晰边界。
官方资料建议放在旁边随时核对:Claude Code 看 官方文档,Python 项目配置看 Python Packaging User Guide,uv 看 Astral uv 文档,测试看 pytest 官方入门,lint 与格式化看 Ruff 文档,API 入门看 FastAPI first steps。
先定义小而明确的完成标准
不要一开始就让 Claude Code 同时生成登录、数据库、Docker、CI 和后台任务。初学阶段变量越多,报错越难判断。第一个版本只需要证明项目能安装、能运行、能测试、能格式化。
uv run pytest通过uv run ruff check .与uv run ruff format .能运行- FastAPI 可以创建并列出一个任务
- CLI 可以从终端添加一个任务
- Claude Code 汇报它执行过的验证命令
flowchart LR
A["任务说明"] --> B["uv 或 venv"]
B --> C["pyproject.toml"]
C --> D["src 实现"]
D --> E["pytest"]
E --> F["ruff"]
F --> G["小步交给 Claude Code 修改"]
这里的任务说明就是给编码智能体的工作单。它应该写清目标、允许修改的文件、Python 版本、验收命令和不能碰的范围。团队协作时,可以结合 CLAUDE.md 最佳实践,把这些规则长期保存下来。
给 Claude Code 的安全提示词
好的提示词不需要华丽,但必须有边界。下面这段可以直接作为第一次任务。
请在这个仓库中添加一个面向 Python 3.12 的小型任务管理 API。
要求:
- 使用 uv 管理依赖,配置集中到 pyproject.toml
- 实现代码放在 src/task_api/,测试放在 tests/
- 用 FastAPI 实现 POST /tasks 与 GET /tasks
- 用 pytest 覆盖正常创建和 404 场景
- ruff check 与 ruff format 必须通过
- 修改前先给计划,修改后报告执行过的验证命令
允许修改:
- pyproject.toml
- src/task_api/**
- tests/**
这段提示词的重点是让 Claude Code 负责“实现加验证”,而不是只吐出代码片段。如果它修改了无关目录,或者没有运行测试,就说明任务边界没有被遵守。做 API 扩展时,可以继续阅读 Claude Code API 开发,测试设计可以参考 Claude Code 测试策略。
用 uv 或 venv 创建环境
新项目推荐优先使用 uv,因为它创建环境快,依赖同步清晰,也适合写成教学材料。公司电脑如果不能安装新工具,就用 Python 自带的 venv。关键不是工具名字,而是项目里只能有一条明确的安装路径。
macOS 或 Linux:
mkdir task-api
cd task-api
uv init --app --python 3.12
uv add "fastapi[standard]"
uv add --dev pytest ruff
mkdir -p src/task_api tests
touch src/task_api/__init__.py
Windows PowerShell:
mkdir task-api
cd task-api
uv init --app --python 3.12
uv add "fastapi[standard]"
uv add --dev pytest ruff
New-Item -ItemType Directory -Force src/task_api, tests
New-Item -ItemType File -Force src/task_api/__init__.py
无法使用 uv 时:
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install "fastapi[standard]" pytest ruff
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install "fastapi[standard]" pytest ruff
把配置集中到 pyproject.toml
初学者最怕配置散落在多个文件里。先把依赖、测试路径和 Ruff 规则集中到 pyproject.toml,Claude Code 后续修改时也更容易遵守同一套规则。
[project]
name = "task-api"
version = "0.1.0"
description = "Small FastAPI and CLI sample for Claude Code practice"
requires-python = ">=3.11"
dependencies = [
"fastapi[standard]>=0.115.0",
]
[project.scripts]
task-api = "task_api.cli:main"
[dependency-groups]
dev = [
"pytest>=8.0.0",
"ruff>=0.8.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/task_api"]
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
select = ["E", "F", "I", "B", "UP"]
实现最小 FastAPI 与 CLI
把下面代码保存到 src/task_api/main.py。它用内存字典保存任务,不适合生产,但非常适合学习路由、Pydantic 校验、响应模型和错误处理。
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
app = FastAPI(title="Task API")
class TaskCreate(BaseModel):
title: str = Field(min_length=1, max_length=80)
class Task(BaseModel):
id: int
title: str
done: bool = False
_tasks: dict[int, Task] = {}
_next_id = 1
@app.post("/tasks", response_model=Task, status_code=201)
def create_task(payload: TaskCreate) -> Task:
global _next_id
task = Task(id=_next_id, title=payload.title)
_tasks[task.id] = task
_next_id += 1
return task
@app.get("/tasks", response_model=list[Task])
def list_tasks() -> list[Task]:
return list(_tasks.values())
@app.patch("/tasks/{task_id}/done", response_model=Task)
def mark_done(task_id: int) -> Task:
task = _tasks.get(task_id)
if task is None:
raise HTTPException(status_code=404, detail="Task not found")
updated = task.model_copy(update={"done": True})
_tasks[task_id] = updated
return updated
uv run fastapi dev src/task_api/main.py
如果还需要命令行入口,把下面代码放到 src/task_api/cli.py。
import argparse
import json
from pathlib import Path
DB_PATH = Path("tasks.json")
def load_tasks() -> list[dict[str, object]]:
if not DB_PATH.exists():
return []
return json.loads(DB_PATH.read_text(encoding="utf-8"))
def save_tasks(tasks: list[dict[str, object]]) -> None:
DB_PATH.write_text(json.dumps(tasks, ensure_ascii=False, indent=2), encoding="utf-8")
def add_task(title: str) -> dict[str, object]:
tasks = load_tasks()
task = {"id": len(tasks) + 1, "title": title, "done": False}
tasks.append(task)
save_tasks(tasks)
return task
def main() -> None:
parser = argparse.ArgumentParser(description="Task CLI")
subparsers = parser.add_subparsers(dest="command", required=True)
add_parser = subparsers.add_parser("add")
add_parser.add_argument("title")
args = parser.parse_args()
if args.command == "add":
task = add_task(args.title)
print(f"Added #{task['id']}: {task['title']}")
if __name__ == "__main__":
main()
uv run task-api add "write pytest"
用 pytest 和 Ruff 保护每次修改
把测试放到 tests/test_main.py。之后每次让 Claude Code 修改,都要求它说明测试结果。
import pytest
from fastapi.testclient import TestClient
from task_api import main
from task_api.main import app
@pytest.fixture(autouse=True)
def clean_tasks() -> None:
main._tasks.clear()
main._next_id = 1
def test_create_and_list_tasks() -> None:
client = TestClient(app)
response = client.post("/tasks", json={"title": "Write pytest"})
assert response.status_code == 201
assert response.json()["title"] == "Write pytest"
list_response = client.get("/tasks")
assert list_response.status_code == 200
assert len(list_response.json()) == 1
def test_mark_done_returns_404_for_missing_task() -> None:
client = TestClient(app)
response = client.patch("/tasks/999/done")
assert response.status_code == 404
assert response.json()["detail"] == "Task not found"
uv run pytest
uv run ruff check .
uv run ruff format .
三个以上真实用例
第一个用例是学习 API。一个小项目就能覆盖路由、校验、自动文档、测试和格式化。第二个用例是业务自动化 CLI,比如整理 CSV、重命名导出文件、生成周报。第三个用例是给旧 Python 脚本补测试,先固定现有行为,再重构。第四个用例是团队培训模板,把相同的 pyproject.toml、src 布局和验证命令交给每位学习者。
这些场景都适合自然连接到 Claude Code 培训与咨询 或 Claude Code 产品模板。读者先获得可运行代码,再看到下一步服务,转化会比单纯推销更自然。
常见失败与避坑
不要只说“做一个 Python 应用”。这样很容易混用 pip、poetry、uv 和 requirements.txt。也不要一开始就加入数据库、JWT 和迁移工具。先让内存版本跑通,再逐步增加持久化。
另一个常见问题是 src 布局导致测试无法 import。请确认 pythonpath = ["src"] 或包安装设置正确。还要避免把真实 API key、客户数据、生产数据库地址贴进提示词。Ruff 的自动修复也要看 diff,不要无脑接受。
实际验证结果
这次重写按 Masa 制作初学者 Python 教材的顺序检查:先固定环境,再集中配置,然后写最小 API,最后用测试与 Ruff 保护迭代。结果很明显:Claude Code 不是因为“更会猜”才可靠,而是因为每次失败都能被定位到环境、import、测试或格式化。对初学者和团队培训来说,这比一次生成大型后端更有价值。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
从Obsidian到CLAUDE.md的Claude Code流程:不再反复解释上下文
把 Obsidian 工作笔记整理成 CLAUDE.md 运行说明,让 Claude Code 每次都带着正确上下文开始。
Claude Code 收入 CTA 路由:从文章分流到 PDF、Gumroad 与咨询
用 Claude Code 按读者意图把文章流量分到免费 PDF、Gumroad 教材或咨询入口。
Claude Code 团队交接规则: 把审查证据、权限、回滚和收入路径一起交付
面向团队的 Claude Code 交接格式: 证据、权限、回滚、免费 PDF、Gumroad 与咨询路径都要可审查。