Python项目结构和打包

Python项目结构和打包

pip install 的本质

PyPI(默认)或其他源(如私有仓库、本地文件)查找指定名称的包,下载对应的.whl文件

whl文件

  • .whlWheel 的缩写,是 Python 的一种标准打包格式(PEP 427 定义)。
  • 它本质上是一个 ZIP 格式的压缩包,扩展名改为 .whl

安装 .whl 文件

1
pip install package_name.whl

hachling

hatchling 是 Python 生态中一个现代的、轻量级的构建后端(build backend),主要用于将 Python 项目打包成可分发的格式(如 .whl 或源码包)。它是 Hatch项目的一部分,由 PyPA(Python Packaging Authority)推荐使用。

在pyproject.toml添加

1
2
3
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

当你运行:

1
2
3
pip install .
# 或
python -m build

如果项目配置了 hatchling 作为构建后端,pipbuild 工具就会调用 hatchling 来完成打包和安装。

例如以下项目结构

1
2
3
4
5
6
7
8
my_project/
├── pyproject.toml
├── src/
│ └── my_utils/ ← 你写的包
│ ├── __init__.py
│ └── math.py
├── scripts/
│ └── run_demo.py ← 想在这里用 my_utils

想让项目的其他文件使用自己编写的包,在pyproject.toml增加

1
2
3
# 👇 新增:显式声明包位置
[tool.hatch.build.targets.wheel]
packages = ["src/my_utils"]

在项目根目录(my_project/)执行:

1
uv pip install -e .

✅ 这会把 src/my_utils/ 注册为一个可导入的包。

src layout

src layout(也称为 src 布局src 目录结构)是 Python 项目中一种推荐的源代码组织方式,其核心思想是:将你的 Python 包(package)放在一个名为 src/ 的子目录下,而不是直接放在项目根目录中。

❌ 传统布局(不推荐)

1
2
3
4
5
6
7
my_project/
├── my_package/
│ ├── __init__.py
│ └── module.py
├── tests/
├── setup.py
└── README.md

src 布局(推荐)

1
2
3
4
5
6
7
8
my_project/
├── src/
│ └── my_package/
│ ├── __init__.py
│ └── module.py
├── tests/
├── pyproject.toml
└── README.md

uv build和uv pip install .什么区别

一、uv build

✅ 作用:构建分发包(不安装)

  • 调用项目的构建后端(如 hatchlingsetuptools 等)。
  • 生成标准的分发文件:
    • 一个 Wheel 文件.whl
    • 一个 源码分发包(sdist,.tar.gz
  • 输出到项目根目录下的 dist/ 文件夹。
  • 不会将包安装到当前 Python 环境中

🔧 示例:

1
uv build

输出:

1
2
dist/my_package-0.1.0-py3-none-any.whl
dist/my_package-0.1.0.tar.gz

image-20251018192905210

二、uv pip install .

✅ 作用:安装当前项目到当前环境

  • 首先(隐式)构建项目(类似 uv build 的过程)。
  • 然后将构建结果安装到当前激活的 Python 环境(如虚拟环境或系统环境)。
  • 安装后,你可以在 Python 中 import 该包。
  • 默认是 “非可编辑安装”(即代码改动不会自动生效,除非重新安装)。

🔧 示例:

1
uv pip install .

效果: - 包被安装到 site-packages/ - 可在 Python 中 import my_package

Editable install(可编辑安装)

Editable install(可编辑安装) 是 Python 包管理中的一种安装模式,它让你在安装一个包的同时,保留对源代码的直接引用

1
uv pip install -e .
  • 核心机制
    • 不复制代码到 site-packages/
    • 而是在 site-packages/ 中创建一个 .pth 文件my_package.egg-link,指向你本地项目中的 src/(或包目录)。
    • Python 解释器在导入时,会顺着这个链接去读你本地的源码。
image-20251018193022973

python是从哪里查找模块的

Python 查找模块(module)的机制由 模块搜索路径(module search path) 决定,这个路径是一个字符串列表,存储在 sys.path 中。当你执行 import some_module 时,Python 会按顺序在这个列表中的每个目录里查找对应的模块文件。

可以通过以下代码查看当前 Python 的模块搜索路径:

1
2
import sys
print(sys.path)

sys.path 通常包含以下几类路径(顺序很重要):

  1. 脚本所在目录(或当前工作目录)
  • 如果你运行 python /path/to/script.py,那么 /path/to/ 会被加到 sys.path[0]
  • 如果你运行 python 进入交互模式,或运行 python -c "...",则当前工作目录(os.getcwd() 会被放在首位。
  • ⚠️ 这是很多“意外导入”问题的根源(比如项目根目录下有同名包)。
  1. 环境变量 PYTHONPATH 中的目录
  • 类似系统的 PATH,你可以通过设置 PYTHONPATH 添加自定义搜索路径。
  • 示例(Linux/macOS):
    1
    2
    export PYTHONPATH="/my/custom/modules:$PYTHONPATH"
    python my_script.py
  • Windows(PowerShell):
    1
    $env:PYTHONPATH = "C:\my\custom\modules;" + $env:PYTHONPATH
  1. 标准库目录
  • Python 自带的模块(如 os, sys, json)所在位置。
  • 通常位于 Python 安装目录下的 lib/ 子目录中。
  1. 第三方包安装目录(site-packages)
  • 通过 pip installuv pip install 等安装的包,会被放到 site-packages 目录。
  • 路径可通过以下命令查看:
    1
    2
    3
    import site
    print(site.getsitepackages()) # 全局环境
    print(site.getusersitepackages()) # 用户级安装
  1. .pth 文件中指定的路径
  • 某些包(尤其是 editable install)会在 site-packages/ 中放置 .pth 文件,动态添加路径到 sys.path

参考资料

build + hatchling 15分钟搞懂Python项目结构和打包_哔哩哔哩_bilibili