2. 开发环境与工程化项目初始化
企业级 Python 后端项目与个人脚本最大的区别,不在于代码量,而在于“可复现”。同一份代码,在同事的机器、CI 流水线和生产容器里必须得到一致的依赖版本、一致的代码风格、一致的检查结果。本节从零搭建一个符合企业规范的后端项目骨架,把虚拟环境、依赖管理、格式化、静态检查、pre-commit 钩子和目录结构串成一条可执行的工程化链路。
一、虚拟环境:隔离是第一原则
不要用系统 Python 直接装依赖。推荐使用 uv 或 poetry 管理虚拟环境与依赖,二者都能锁定版本并生成锁文件。以 uv 为例,初始化项目并指定 Python 版本:
uv init backend-skeleton --python 3.12
cd backend-skeleton
uv venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
虚拟环境目录 .venv/ 必须写入 .gitignore,绝不提交。团队协作时,每个人通过锁文件重建环境,而不是互相拷贝 site-packages。
二、依赖管理:区分运行时与开发依赖
企业项目要把“运行时依赖”和“开发工具依赖”分开。运行时依赖是服务启动必需的,如 fastapi、sqlalchemy、pydantic;开发依赖只在本地和 CI 使用,如 ruff、mypy、pytest、pre-commit。使用 uv 添加依赖:
uv add fastapi "uvicorn[standard]" sqlalchemy pydantic-settings
uv add --dev ruff mypy pytest pre-commit
执行后 pyproject.toml 会记录依赖声明,uv.lock 记录精确版本与哈希。锁文件必须提交到仓库,它是“可复现”的核心凭证。CI 中使用 uv sync --frozen 安装,若锁文件与声明不一致会直接失败,避免“本地能跑、线上报错”。
三、代码格式化与静态检查
格式化解决“长什么样”,静态检查解决“有没有错”。推荐 ruff 同时承担格式化与 lint,mypy 负责类型检查。在 pyproject.toml 中集中配置:
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "I", "B", "UP", "SIM"]
[tool.mypy]
python_version = "3.12"
strict = true
warn_unused_ignores = true
其中 I 负责 import 排序,B 捕获常见 bug,UP 提示可用新语法,SIM 简化冗余写法。mypy 开启 strict 后,未标注类型的函数、隐式 Any、未使用的 type: ignore 都会报错。执行方式:
uv run ruff format .
uv run ruff check . --fix
uv run mypy src
判断标准很明确:ruff format 后无 diff,ruff check 退出码为 0,mypy 输出 Success: no issues found。任何一项不通过,都不应进入提交环节。
四、pre-commit:把检查前移到提交前
靠人自觉执行检查不可靠,pre-commit 钩子能在 git commit 时自动拦截不合格代码。创建 .pre-commit-config.yaml:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
hooks:
- id: mypy
additional_dependencies: [pydantic]
安装并运行:
uv run pre-commit install
uv run pre-commit run --all-files
钩子执行顺序是:先 ruff --fix 自动修复可修复问题,再 ruff-format 统一格式,最后 mypy 做类型检查。若 ruff 修改了文件,本次提交会被中止,需要重新 git add 后再提交。这是正常行为,不是报错。
五、项目目录结构规范
推荐采用 src 布局,把业务代码放在 src/ 下,避免测试时误导入根目录同名模块。一个可直接使用的骨架如下:
backend-skeleton/
├── pyproject.toml
├── uv.lock
├── .pre-commit-config.yaml
├── .gitignore
├── README.md
├── src/
│ └── app/
│ ├── __init__.py
│ ├── main.py # FastAPI 入口
│ ├── core/
│ │ ├── config.py # pydantic-settings 配置
│ │ └── logging.py
│ ├── api/
│ │ └── v1/
│ │ ├── router.py
│ │ └── endpoints/
│ │ └── health.py
│ ├── models/ # ORM 模型
│ ├── schemas/ # Pydantic 模型
│ └── services/ # 业务逻辑
└── tests/
├── conftest.py
└── test_health.py
分层职责要清晰:api 只做参数校验和响应组装,services 承载业务逻辑,models 与 schemas 分别对应数据库和接口契约。这样后续接入数据库、缓存、消息队列时,改动范围可控。
六、最小可运行验证
在 src/app/main.py 写入一个健康检查接口:
from fastapi import FastAPI
app = FastAPI(title="backend-skeleton")
@app.get("/health")
def health() -> dict[str, str]:
return {"status": "ok"}
启动并验证:
uv run uvicorn app.main:app --reload --port 8000
curl http://127.0.0.1:8000/health
# {"status":"ok"}
当 curl 返回 {"status":"ok"},且 pre-commit run --all-files 全部通过,说明骨架已具备企业项目的基本工程能力:环境可复现、依赖可锁定、风格可统一、错误可拦截、结构可扩展。
uv run ruff format . && uv run ruff check . && uv run mypy src 写进 Makefile 或 justfile,本地与 CI 调用同一条命令,避免“本地通过、CI 失败”的差异。.venv/、__pycache__/、.mypy_cache/、.ruff_cache/ 提交到仓库;同时确保 uv.lock 与 .pre-commit-config.yaml 必须提交,否则团队无法复现环境与检查规则。