src/目录非初始必需,仅当打包发布、ci报错或import失败时才需引入;__init__.py在需相对导入或版本管理时必须保留;tests/应置于根目录,配置与资源须分离并用importlib.resources加载。

为什么 src/ 目录不是一开始就必须加的
刚跑通第一个 python main.py 时,把所有代码塞进项目根目录完全没问题。这时候加 src/ 反而多一层路径跳转,import 写起来还容易错。等你开始写测试、打包、或者被 CI 报 ModuleNotFoundError 卡住,才是考虑挪进去的信号。
常见错误现象:pytest 能跑通,但 pip install -e . 后在别处 import mypackage 失败;或者 python -m mypackage 提示找不到模块。
- 真正触发
src/的典型场景:需要发布到 PyPI,或团队里有人习惯从任意路径运行你的包 -
setup.py或pyproject.toml里packages=find_packages()默认不扫描src/,得显式写成find_packages(where="src")并配package_dir={"": "src"} - 用
src/后,本地开发时 IDE 可能默认不把src/加进PYTHONPATH,要手动设或靠pyproject.toml的[tool.setuptools]配置补上
__init__.py 什么时候可以删、什么时候必须留
Python 3.3+ 支持隐式命名空间包,但现实里删 __init__.py 很容易翻车——尤其当你的包要被 importlib.util.spec_from_file_location 或某些打包工具(如 pyinstaller)处理时。
使用场景差异明显:纯数据包(比如只放 JSON/YAML 配置)可尝试无 __init__.py;但只要涉及 from . import xxx、相对导入、或想用 pkg_resources 查版本,它就得在。
立即学习“Python免费学习笔记(深入)”;
- 删了
__init__.py后,pip install -e .可能成功,但importlib.import_module("mypackage.submod")报ModuleNotFoundError -
__init__.py为空文件也行,但建议至少写__version__ = "0.1.0",方便后续统一管理版本 - 如果用了
pyproject.toml+setuptools,且包结构是src/mypackage/__init__.py,那src/下不能有__init__.py,否则会被当成另一个顶层包
测试目录放哪?tests/ 和 src/tests/ 哪个更实际
把 tests/ 放项目根目录是主流选择,不是因为“规范”,而是因为 pytest 默认就扫这个路径,tox 和 CI 脚本也认它。放进 src/ 里反而让测试变成“被安装的代码”,既没必要又可能引发权限或打包问题。
容易踩的坑在于路径假设:很多新手写 from mypackage import foo 在测试里跑得通,但一换机器或改 sys.path 就崩——其实只是当前工作目录碰巧在项目根下。
- 确保
pytest运行时,src/在PYTHONPATH中(推荐用pyproject.toml的[tool.pytest.ini_options]配pythonpath = ["src"]) - 不要在
tests/里写sys.path.insert(0, "../src"),这种硬编码路径在 CI 或不同操作系统下极不可靠 - 如果真要隔离测试依赖,用
conftest.py里的pytest_configure动态加路径,比改sys.path更可控
配置文件和静态资源该不该跟代码混放
开发阶段放一起省事,但上线后,配置(尤其是密钥、环境变量)和静态资源(模板、CSS、图片)必须分离。硬编码路径如 os.path.join(os.path.dirname(__file__), "../config.yaml") 是典型坏味道——一旦打包成 wheel 或用 zipimport,__file__ 指向的就是 zip 包内路径,根本找不到上级目录。
真实痛点常出现在 Docker 部署:镜像里只复制了 src/ 和 pyproject.toml,结果运行时报 FileNotFoundError: [Errno 2] No such file or directory: 'config.yaml'。
- 配置文件优先走环境变量(
os.getenv("DB_URL")),次选命令行参数,最后才读文件;文件路径用importlib.resources.files("mypackage").joinpath("config.yaml")(Python 3.9+)或pkg_resources.resource_filename("mypackage", "config.yaml")(兼容旧版) - 静态资源(如 Jinja2 模板)别放在
src/mypackage/下跟 Python 文件混着,单独建templates/或static/根目录,用importlib.resources加载 - 如果用了
MANIFEST.in,记得加include *.yaml *.j2 *.css,否则pip install后这些文件根本不会进安装目录
目录结构不是越“标准”越好,而是越贴近你下一个部署场景越稳。很多人卡在“不知道该不该动”,其实答案就藏在下一次 pip install 或 docker build 报错的那行日志里。










