第3章:目录结构设计

说到项目目录结构,我见过太多新手上来就写代码,写到一半发现配置文件没地方放,测试代码和业务代码混在一起,最后整个项目乱成一锅粥。嗯,今天我们就来聊聊怎么把这件事做对。

3.1 标准Python项目目录结构

先给你看一个我用了多年的标准结构。说实话,这个结构不是我发明的,而是从多个开源项目里提炼出来的。你想想看,能同时被Django、Flask、Requests这些知名项目采用的结构,肯定有它的道理。

my_project/
├── src/
│   └── my_project/
│       ├── __init__.py
│       ├── main.py
│       ├── config.py
│       ├── models/
│       ├── services/
│       └── utils/
├── tests/
│   ├── __init__.py
│   ├── test_main.py
│   └── test_services/
├── scripts/
│   ├── setup.sh
│   └── deploy.sh
├── docs/
├── requirements/
│   ├── base.txt
│   ├── dev.txt
│   └── prod.txt
├── .env.example
├── .gitignore
├── Makefile
├── pyproject.toml
└── README.md

为什么要有两层 src/my_project?我刚开始也觉得多此一举。直到有一次,我在一个大型微服务项目里,发现多个包名冲突,改得我头皮发麻。后来才明白,这层 src 目录其实是个命名空间保护伞。

核心原则:src 目录只放源码,tests 目录只放测试,两者互不干扰。这是项目健康度的基本保障。

3.2 src与tests分离

为什么要分离?说白了就三个字:解耦合。测试代码和业务代码混在一起,就像把厨房和厕所建在一个房间里——虽然能用,但总觉得不对劲。

我曾经接手过一个项目,测试文件散落在各个业务模块里。想跑个单元测试,得先搞清楚哪些是测试文件,哪些是业务文件。更可怕的是,有人把测试代码直接 import 到了生产环境里……嗯,那场面,你懂的。

3.2.1 测试目录的最佳实践

目录 用途 注意事项
tests/unit 单元测试 测试单个函数或类
tests/integration 集成测试 测试模块间交互
tests/e2e 端到端测试 模拟真实用户场景
tests/fixtures 测试数据 存放 mock 数据和配置文件

我的小技巧:测试文件的命名尽量和源码保持一致。比如 src/my_project/services/user_service.py 对应 tests/unit/services/test_user_service.py。这样找测试文件时,闭着眼睛都能摸到。

3.3 配置文件与脚本的放置

配置文件放哪里?这个问题看似简单,但坑不少。我见过有人把数据库密码直接写在代码里,也见过把配置文件塞进版本控制系统的。嗯,这些都是血泪教训。

3.3.1 配置文件的分类

我个人习惯把配置文件分成三类:

  1. 环境配置:数据库连接、、环境变量等。这些用 .env 文件管理,不提交到 Git。
  2. 应用配置:日志级别、缓存策略、超时时间等。放在 config/ 目录下,用 YAML 或 TOML 格式。
  3. 构建配置:依赖管理、打包参数等。用 pyproject.toml 或 setup.cfg。
# .env 示例(不要提交到 Git)
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
SECRET_KEY=your-secret-key-here
DEBUG=True

# config/app.yaml 示例(可以提交到 Git)
server:
  host: "0.0.0.0"
  port: 8000
  workers: 4
logging:
  level: "INFO"
  format: "json"

注意:千万不要把 .env 文件提交到 Git 仓库!我曾经见过一个团队,因为不小心把生产环境的 .env 提交了,导致数据库密码泄露。正确的做法是提交 .env.example,里面只放占位符。

3.3.2 脚本文件的组织

脚本文件我习惯放在 scripts/ 目录下。为什么不用 bin/ 或 tools/?其实都可以,但 scripts/ 更直观,一看就知道是辅助脚本。

常见的脚本类型:

  • setup.sh:项目初始化脚本,安装依赖、创建虚拟环境
  • deploy.sh:部署脚本,构建镜像、推送服务器
  • migrate.sh:数据库迁移脚本
  • clean.sh:清理临时文件、缓存
# scripts/setup.sh
#!/bin/bash
# 项目初始化脚本

echo "Creating virtual environment..."
python3 -m venv venv

echo "Activating virtual environment..."
source venv/bin/activate

echo "Installing dependencies..."
pip install -r requirements/dev.txt

echo "Setting up pre-commit hooks..."
pre-commit install

echo "Done! Run 'source venv/bin/activate' to start."

3.4 目录结构的演进

你可能会问:这个结构是不是一成不变的?当然不是。项目从小到大会经历几个阶段:

阶段 目录结构 特点
原型阶段 单个 Python 文件 快速验证想法
小型项目 src + tests 分离 开始关注可维护性
中型项目 增加 scripts、docs、config 团队协作需求出现
大型项目 多模块、微服务 需要独立部署和版本管理

我个人建议,从第一天就用标准结构。别想着「等项目大了再重构」——重构的成本往往比你想象的高得多。

3.5 避坑指南

最后,分享几个我踩过的坑:

  • 不要用 src/ 作为包名:from src import xxx 这种写法很丑,而且容易和系统路径冲突。
  • 测试目录不要加 __init__.py:除非你用了 pytest 的某些特殊功能,否则不需要。
  • 配置文件不要硬编码路径:用环境变量或命令行参数传递,这样部署时更灵活。
  • 脚本文件要加执行权限:chmod +x scripts/*.sh,不然别人用的时候会一脸懵。

总结一下:好的目录结构就像好的房间布局——你不需要每天想着它,但它能让你的生活顺畅很多。花 10 分钟设计好结构,能省下未来 100 小时的调试时间。这笔账,怎么算都划算。