第四章 项目目录结构:src/、configs/、data/、tests/ 的黄金布局

第四章 项目目录结构:src/、configs/、data/、tests/ 的黄金布局

    • [0. 先说结论:目录结构的目标不是"好看",而是"五可"](#0. 先说结论:目录结构的目标不是“好看”,而是“五可”)
    • [1. 为什么必须采用 src/ 布局(而不是"随便放.py")](#1. 为什么必须采用 src/ 布局(而不是“随便放.py”))
    • [2. 黄金布局全貌:一眼看懂工程项目的骨架](#2. 黄金布局全貌:一眼看懂工程项目的骨架)
    • [3. 四大核心目录:你必须知道每个目录的边界](#3. 四大核心目录:你必须知道每个目录的边界)
    • [3.1 src/:项目唯一真相(Single Source of Truth)](#3.1 src/:项目唯一真相(Single Source of Truth))
      • [src/ 的三条硬规则](#src/ 的三条硬规则)
      • [Mermaid:src/ 的调用关系建议](#Mermaid:src/ 的调用关系建议)
    • [3.2 configs/:参数从代码里拔出来,项目才可迁移](#3.2 configs/:参数从代码里拔出来,项目才可迁移)
    • [3.3 data/:数据必须分层,否则你永远不知道"哪个是最终结果"](#3.3 data/:数据必须分层,否则你永远不知道“哪个是最终结果”)
    • [3.4 tests/:你不需要高覆盖率,但必须能防止"自己坑自己"](#3.4 tests/:你不需要高覆盖率,但必须能防止“自己坑自己”)
      • [tests/ 最小要求(建议你抄走)](#tests/ 最小要求(建议你抄走))
    • [4. scripts/ 与 notebooks/:它们的定位必须明确](#4. scripts/ 与 notebooks/:它们的定位必须明确)
    • [5. runs/ 与 reports/:交付闭环的关键,不是摆设](#5. runs/ 与 reports/:交付闭环的关键,不是摆设)
    • [6. 常见坑与"强制规避策略"](#6. 常见坑与“强制规避策略”)
      • [坑 1:把路径写死在代码里](#坑 1:把路径写死在代码里)
      • [坑 2:把业务逻辑塞进 scripts](#坑 2:把业务逻辑塞进 scripts)
      • [坑 3:data/ 里混进 runs 产物](#坑 3:data/ 里混进 runs 产物)
      • [坑 4:Notebook 变成垃圾场](#坑 4:Notebook 变成垃圾场)
    • [7. 本章最低交付(MDR):你必须交付什么?](#7. 本章最低交付(MDR):你必须交付什么?)
    • [8. 小结:目录结构不是形式,是你长期复利的起点](#8. 小结:目录结构不是形式,是你长期复利的起点)

(从"脚本堆"升级为"可维护、可复现、可交付的工程项目")

如果你做过两三个数据分析/AI 项目,你一定见过下面这些目录:

  • final.ipynbfinal2.ipynbfinal_final.ipynb
  • data.xlsxdata_new.xlsxdata_new(1).xlsx
  • model.pkl 放在桌面,或者散落在各个子目录
  • 配置写死在代码里:路径、阈值、超参、API Key 都是常量
  • 合作者拉仓库后第一句话是:"怎么跑?"

这不是你不认真,而是你缺一个"工程化的默认布局"。

目录结构是工程纪律的第一层:它决定了你的项目会不会越做越乱,能不能复现,能不能交付。

本章我给你一个在科研/数据分析/AI 工程都通用的"黄金布局":
src/、configs/、data/、tests/ 为核心,配合 runs/、reports/ 做闭环。


0. 先说结论:目录结构的目标不是"好看",而是"五可"

你搭目录结构不是为了优雅,而是为了让项目具备以下能力:

  • 可维护:代码模块边界明确,改动不牵一发动全身
  • 可复现:配置、数据版本、运行产物可追踪
  • 可评估:评测脚本与测试数据清晰,指标可回放
  • 可扩展:新增功能、数据源、模型不会导致结构崩塌
  • 可交付:别人 5 分钟内能跑起来,10 分钟内看懂产物

这也是本专栏的"学习协议"落地方式之一:结构先行,后面才谈效率与技巧。


1. 为什么必须采用 src/ 布局(而不是"随便放.py")

很多人会把 Python 包直接放在项目根目录:

text 复制代码
project/
  myproj/
  notebooks/
  data/

这会带来两个长期隐患:

  1. 导入行为不稳定

    Notebook 工作目录不同、运行入口不同,import myproj 可能指向不同位置,出现"能跑但解释不清"的玄学问题。

  2. 同名污染

    根目录里出现 utils.pyrandom.pyjson.py 这种文件名,很容易与标准库或第三方库冲突。

采用标准的 src/ 布局,能显著降低这些问题:

text 复制代码
project/
  src/
    myproj/
      __init__.py
      ...

你会发现:当代码只从 src/ 导入,Notebook、脚本、测试、CLI 的行为更一致,团队协作也更稳定。


2. 黄金布局全貌:一眼看懂工程项目的骨架

先给你一个完整布局(你可以直接复制作为项目模板):

text 复制代码
project/
  README.md
  pyproject.toml              # 或 requirements.txt(入门阶段也可)
  .gitignore

  src/
    myproj/
      __init__.py
      io/
        loaders.py            # 数据加载、路径规范、输入校验
      core/
        config.py             # 配置加载(YAML/ENV)
        logging.py            # 日志初始化
        errors.py             # 异常分层
      etl/
        clean.py              # 清洗模板
        audit.py              # 审计与数据质量摘要
      features/
        build.py              # 特征工程
      modeling/
        train.py              # 训练
        infer.py              # 推理
        metrics.py            # 指标与评估
      viz/
        charts.py             # 图表模板

  configs/
    default.yaml
    dev.yaml
    prod.yaml                 # 若有部署场景

  data/
    raw/                      # 原始数据(只读)
    interim/                  # 中间产物(可删)
    processed/                # 清洗后/特征后数据
    external/                 # 外部来源数据(可选)

  notebooks/
    00_index.ipynb
    01_eda.ipynb
    02_pipeline.ipynb

  tests/
    test_etl_clean.py
    test_metrics.py

  scripts/
    run_etl.py
    run_train.py
    run_eval.py

  runs/
    2026-01-10_1030/
      params.json
      metrics.json
      log.txt
      artifacts/

  reports/
    figures/
    tables/
    report.md

这套结构的核心是:

  • src/ 放"可复用的真逻辑"
  • configs/ 放"可迁移的参数与环境差异"
  • data/ 分层管理数据生命周期
  • tests/ 保障长期可维护性
  • runs/reports/ 负责交付闭环

3. 四大核心目录:你必须知道每个目录的边界

下面我们逐一拆解 src/、configs/、data/、tests/ 的"黄金规则"。


3.1 src/:项目唯一真相(Single Source of Truth)

src/ 里放的是:可复用、可测试、可维护的核心代码。

src/ 的三条硬规则

规则 1:Notebook 不允许出现核心逻辑

Notebook 可以调用 src/,但核心逻辑必须下沉。

规则 2:入口脚本不写业务逻辑
scripts/run_train.py 只负责:解析参数 → 调用 src/ → 落盘输出。

真正的训练逻辑在 src/myproj/modeling/train.py

规则 3:模块按职责划分,不按步骤划分

不推荐 step1.py/step2.py,推荐 etl/clean.pymodeling/train.py 这种"职责稳定"的结构。

Mermaid:src/ 的调用关系建议

scripts/run_train.py 入口
myproj.core.config 读取配置
myproj.io.loaders 读取数据
myproj.etl.clean 清洗
myproj.features.build 特征工程
myproj.modeling.train 训练
myproj.modeling.metrics 评估
runs// 产物落盘

你只要坚持这个结构,项目就不会失控。


3.2 configs/:参数从代码里拔出来,项目才可迁移

configs/ 的目标是:让"同一份代码"在不同环境下工作。

典型要放进配置的东西包括:

  • 数据路径与输入格式
  • 清洗规则(列名、缺失值策略、阈值)
  • 模型超参(seed、训练轮数、阈值)
  • 输出目录(runs/reports 路径)
  • 日志级别(INFO/DEBUG)

配置管理的最小建议(别一开始搞复杂)

你可以从 configs/default.yaml 开始,未来再扩展多环境配置。

yaml 复制代码
# configs/default.yaml
project:
  name: myproj
  run_dir: runs

data:
  input_path: data/raw/sample.csv
  output_path: data/processed/clean.csv

train:
  seed: 42
  test_size: 0.2

logging:
  level: INFO

关键收益:

当你把参数写进 YAML,你的项目就具备了"可迁移性"和"可复现记录能力"(因为 params 可以落盘)。


3.3 data/:数据必须分层,否则你永远不知道"哪个是最终结果"

数据目录最常见的灾难是:所有文件混在一起,谁也不知道哪个可删,哪个是原始输入,哪个是最终结果。

我建议用"四层"模型,足够通用:

  • raw/:原始数据,只读,不修改
  • interim/:中间产物,可删(cache)
  • processed/:清洗后、可复用的数据
  • external/:外部数据源(可选)

Mermaid:数据生命周期示意

data/raw 原始输入
etl.clean 清洗
data/processed 清洗后数据
features.build 特征数据
model.train 模型与指标
runs/ 运行产物
reports/ 可交付报告

一句话规则:

raw 不动,processed 可复用,interim 可随时重建。


3.4 tests/:你不需要高覆盖率,但必须能防止"自己坑自己"

很多同学抗拒测试,觉得浪费时间。我的观点很明确:

科研/数据项目不追求 90% 覆盖率,但必须有最小测试来防回归。

tests/ 最小要求(建议你抄走)

至少覆盖三类:

  1. 数据契约测试:列名、类型、缺失率是否符合预期
  2. 关键函数测试:清洗、特征、指标计算的核心逻辑
  3. 回归测试:同样输入时,关键指标不应无故漂移

示例测试(非常简化版):

python 复制代码
def test_clean_keeps_required_columns():
    import pandas as pd
    from myproj.etl.clean import clean_df

    df = pd.DataFrame({"a": [1, None], "b": [2, 3]})
    out = clean_df(df)
    assert set(out.columns) == {"a", "b"}

你只要坚持写这种"最小断言",项目长期可维护性会明显提升。


4. scripts/ 与 notebooks/:它们的定位必须明确

很多项目乱,是因为 scripts/notebooks/ 混入了业务逻辑。

scripts/:只做入口,不做业务

scripts 的职责是:

  • 解析参数
  • 读取配置
  • 调用 src
  • 落盘 runs/reports

notebooks/:只做叙事与探索

Notebook 的职责是:

  • 探索数据
  • 展示过程
  • 解释结果
  • 调参对比(但核心逻辑仍在 src)

你可以把 Notebook 当成"论文的叙事载体",把 src 当成"真正可交付的工程"。


5. runs/ 与 reports/:交付闭环的关键,不是摆设

如果你只搭了 src/configs/data/tests,却没有 runs/reports,你依然会在交付时被卡住。原因是:你缺"证据链"。

runs/:一次运行 = 一个可追溯目录

建议命名:runs/YYYY-MM-DD_hhmm/

必须包含(最低要求):

  • params(本次运行用的配置快照)
  • metrics(指标输出)
  • log(日志)
  • artifacts(模型、图表、导出文件)

reports/:面向人类的交付物

  • figures/ 图
  • tables/ 表
  • report.md / report.pdf

你要区分清楚:

runs 是给你自己和机器看的;reports 是给导师/客户/评审看的。


6. 常见坑与"强制规避策略"

坑 1:把路径写死在代码里

策略:路径必须进 configs,并由 core/paths.py 统一解析。

坑 2:把业务逻辑塞进 scripts

策略:scripts 只允许 80 行以内,超过就说明你在写业务逻辑了,必须下沉 src。

坑 3:data/ 里混进 runs 产物

策略:运行产物只进 runs,不进 data。data 只放"可复用数据"。

坑 4:Notebook 变成垃圾场

策略:Notebook 超过 10 个且没有编号,基本宣告失控。

强制用 00_ 01_ 02_ 编号,且每个 Notebook 都应对应一个明确目的。


7. 本章最低交付(MDR):你必须交付什么?

按照专栏"学习协议",本章完成后你至少交付:

  1. 一个符合黄金布局的项目骨架(含 src/configs/data/tests)
  2. 一个能跑通的最小流水线:读入 → 清洗 → 输出(哪怕只处理 sample 数据)
  3. 一次 runs 落盘(params/log/artifacts 至少具备)
  4. 一个最小 pytest 测试(至少 1 个断言)
  5. README 写清楚:如何安装、如何运行、输出在哪里

你做到这些,你的项目就真正开始"像工程"了。


8. 小结:目录结构不是形式,是你长期复利的起点

你会发现:当你采用标准目录结构后,很多问题会自动消失:

  • 导入不再玄学
  • 参数不再散落
  • 数据不再混乱
  • 运行产物可追溯
  • 合作者知道怎么跑
  • 作品集看起来像真实工程项目

下一章我们会把 configs 再进一步工程化:用 YAML/ENV 做配置管理,让项目真正做到"可迁移"。

《第五章 配置管理:用 YAML/ENV 让项目可迁移》

如果你愿意,把你现在项目的目录结构贴出来(截图或文字都行),我可以按本章的黄金布局给你做一次"最小改造方案":保留你现有内容,尽量少移动文件,但让项目立刻具备可维护与可复现的工程结构。

相关推荐
爱笑的眼睛114 小时前
超越可视化:降维算法组件的深度解析与工程实践
java·人工智能·python·ai
清铎5 小时前
leetcode_day12_滑动窗口_《绝境求生》
python·算法·leetcode·动态规划
ai_top_trends5 小时前
2026 年工作计划 PPT 横评:AI 自动生成的优劣分析
人工智能·python·powerpoint
TDengine (老段)5 小时前
TDengine Python 连接器进阶指南
大数据·数据库·python·物联网·时序数据库·tdengine·涛思数据
brent4236 小时前
DAY50复习日
开发语言·python
万行6 小时前
机器学习&第三章
人工智能·python·机器学习·数学建模·概率论
Data_agent6 小时前
Cocbuy 模式淘宝 / 1688 代购系统(欧美市场)搭建指南
开发语言·python
m0_726365836 小时前
哈希分分预测系统 打造自适应趋势分析「Python+DeepSeek+PyQt5」
python·qt·哈希算法
vyuvyucd6 小时前
Qwen-1.8B-Chat昇腾Atlas800TA2部署实战
python