随着 Python 项目逐渐向工程化与团队协作方向发展,项目管理方式也经历了从最初的 venvconda 到如今基于 Rust 编写的 uv 工具的演进。

本文将系统梳理三种主流 Python 项目管理方案的差异与适用场景,并以实际配置文件为例,带你理解 pyproject.toml 这一现代 Python 项目的核心。

一、从最原始的虚拟环境:venv

对于早期的 Python 项目而言,venv 是最直接的虚拟环境管理工具。具体的操作命令如下:

# 创建虚拟环境
python -m venv venv

# 激活环境
# Linux / macOS
source venv/bin/activate
# Windows
venv\Scripts\activate

# 退出环境
deactivate

✅ 优点

  • 轻量、原生、无外部依赖。

  • 随 Python 自带,环境独立性较好。

❌ 缺点

  • 无法指定不存在的 Python 版本(例如项目要求 Python 3.12,但系统只有 3.10)。

  • 不支持环境列表或切换管理。

  • 无法管理系统级依赖(如 C 库)。

👉 适用场景:简单脚本或单人项目。

二、科学计算的黄金搭档:conda

在数据科学、机器学习等需要依赖底层 C 库或非 Python 工具的领域,conda 几乎是事实标准。

# 创建并激活虚拟环境
conda create -n myenv python=3.10
conda activate myenv

✅ 优点

  • 可以自由指定 Python 版本。

  • 同时管理 Python 包与系统级依赖(C/C++ 库、R、CUDA等)。

  • 强大的环境隔离能力。

  • 论文复现一般都需要用conda。

❌ 缺点

  • 环境通常安装在系统目录,不是项目级管理。

  • 在部署和协作中不如 npmuv 那样轻量、可复现。

👉 适用场景:科学计算、数据分析、深度学习项目。

三、Rust 加持的新宠:uv

uv 是由 Astral 团队基于 Rust 开发的 Python 项目管理工具,主打速度、简洁与现代化依赖管理
它融合了 pipvirtualenvpoetry 等工具的优点,可以理解为 Python 生态的“npm + nvm + pipenv 合体版”

🧩 三种使用方式

1. 兼容 venv 模式(适合已有项目)

uv venv myenv --python=3.10
source myenv/bin/activate
uv pip install numpy

2. 项目模式(推荐)

类似于 Node.js 的 npmpnpm,通过 pyproject.toml 管理依赖。

uv init
uv add numpy
uv run main.py
  • uv run 会自动创建 .venv 文件夹并同步依赖。

  • 删除 .venv 即可清理环境,结构清晰。

3. 工具模式(管理 CLI 工具)

uv tool install --python 3.12 pdf2zh
uv tool run pdf2zh input.pdf
  • 各命令行工具相互隔离,不污染系统环境。

  • 类似 npm install -g 的体验,但更干净。

🚀 优点总结

特性

uv 表现

安装速度

⚡ 并行安装,比 pip/conda 快得多

配置文件

pyproject.toml + uv.lock,现代且标准

可复现性

完全可锁定依赖版本

清理

删除 .venv 即删环境

可扩展性

兼容 venv / pip / requirements.txt

四、pyproject.toml:现代 Python 项目的核心

PEP 518 起,Python 官方推荐使用 pyproject.toml 管理项目元数据。它逐步取代了老旧的 setup.py,成为统一的构建与依赖声明标准

为什么取代 setup.py

  • setup.py 是可执行脚本,存在安全隐患;

  • 配置与逻辑混杂,不便静态分析;

  • 缺乏标准依赖格式,工具兼容性差。

pyproject.toml 的优势是:

  • 使用易读的 TOML 格式;

  • 配置与逻辑分离;

  • 被 Poetry、Hatch、uv 等现代工具全面支持。


🧱 示例:字节的 trae-agent 项目配置

定义项目的基本信息,用于打包和发布:

[project]
name = "trae-agent"
version = "0.1.0"
description = "LLM-based agent for general purpose software engineering tasks"
requires-python = ">=3.12"
dependencies = [
    "openai>=1.86.0",
    "anthropic>=0.54.0",
    "click>=8.0.0",
    "pydantic>=2.0.0",
    "ruff>=0.12.4",
]

可选依赖分组(如 test、evaluation)用于开发、测试等场景

可通过 pip install 项目名[分组名] 安装 (如 pip install trae-agent[test])

[project.optional-dependencies]
test = ["pytest>=8.0.0", "pre-commit>=4.2.0"]
evaluation = ["datasets>=3.6.0", "docker>=7.1.0"]

定义命令行入口,格式为: 命令名 = "模块路径:函数名"。

[project.scripts]
trae-cli = "trae_agent.cli:main"

现代工具配置(如 ruff, pytest, coverage)也统一放入:

# 代码覆盖率工具 coverage.py 的配置:
# source:指定需要计算覆盖率的源码目录(如 ["trae_agent"])。
# omit:排除不需要计算覆盖率的目录(如 ["tests/*"] 排除测试文件)。
[tool.coverage.run]
source = ["trae_agent"]
omit = ["tests/*"]
# exclude_lines:覆盖率报告中忽略的代码行
#(如 pragma: no cover 标记的行、__repr__ 方法等)。
[tool.coverage.report]
exclude_lines = [
    "pragma: no cover",
    "def __repr__",
    "if self.debug:",
    "if settings.DEBUG",
    "raise AssertionError",
    "raise NotImplementedError",
    "if 0:",
    "if __name__ == .__main__.:",
    "class .*\\bProtocol\\):",
    "@(abc\\.)?abstractmethod",
]

# 代码检查与格式化工具 ruff 的配置:
# line-length:每行代码的最大长度(如 100)。
# lint.select:启用的 lint 规则集
#(如 B 对应 bugbear 规则、F 对应 pyflakes 规则等)
# 用于检查代码风格和潜在错误。
[tool.ruff]
line-length = 100

[tool.ruff.lint]
select = [
    "B",
    "SIM",
    "C4",
    "E4", "E9", "E7", "F",
    "I"
]

构建系统配置、工具配置、pytest测试框架的配置:

[build-system]
# 构建项目时需要的工具, hatchling是一种现代 Python 构建后端
requires = ["hatchling"]
# 指定构建后端(如 hatchling.build),负责处理打包逻辑。
build-backend = "hatchling.build"

# 工具配置:为各种开发工具(如测试、格式化、 lint 工具)提供配置
# 不同工具用 tool.工具名 区分。
[tool.hatch.build.targets.wheel]
# 指定打包到 wheel 中的包(如 ["trae_agent"] 表示只打包 trae_agent 目录)。
packages = ["trae_agent"]

# pytest 测试框架的配置
[tool.pytest.ini_options]
minversion = "6.0"
# 默认命令行参数
addopts = "-ra -q --strict-markers"
testpaths = [
    "tests",
]
asyncio_mode = "auto"
# 定义测试标记(如 slow、integration)
# 用于筛选测试用例(如 pytest -m "not slow" 跳过慢测试)。
markers = [
    "slow: marks tests as slow (deselect with '-m \"not slow\"')",
    "integration: marks tests as integration tests",
    "unit: marks tests as unit tests",
]

五、离线/内网环境下使用 uv

若你的服务器无法联网,可通过手动安装 uv 与离线 Python 构建包实现部署。

1. 下载与解压

GitHub Releases 下载对应平台包:

tar -xzf uv-i686-unknown-linux-gnu.tar.gz -C ~/
mv uv-i686-unknown-linux-gnu uv-linux

2. 添加环境变量与镜像源

推荐国内镜像:

# ~/.bashrc
export PATH="~/uv-linux:$PATH"
export UV_DEFAULT_INDEX="https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"

source ~/.bashrc
uv --version

3. 安装独立 Python 版本

使用 python-build-standalone

mkdir ~/uv-python
tar -xzf cpython-3.12.11+20250814-x86_64-unknown-linux-gnu-install_only.tar.gz -C ~/uv-python
export PATH="~/uv-python/python/bin:$PATH"
uv python list --no-managed-python

若能正常显示 Python 版本,即表示配置成功。

六、推荐项目结构模板(基于 uv

以下是一个推荐的现代 Python 工程模板,既适合小型工具项目,也能扩展为大型应用:

my-project/
├── .venv/                 # uv 自动生成的虚拟环境(可忽略进版本控制)
├── src/                   # 主代码目录
│   └── my_project/
│       ├── __init__.py
│       ├── main.py
│       └── utils.py
├── tests/                 # 测试代码
│   ├── __init__.py
│   └── test_main.py
├── pyproject.toml         # 项目配置与依赖声明
├── uv.lock                # 依赖锁定文件(自动生成)
├── README.md              # 项目文档
└── .gitignore             # Git 忽略规则

初始化流程

# 1. 初始化项目
uv init my-project
cd my-project

# 2. 添加依赖
uv add requests rich pytest

# 3. 运行代码(自动激活虚拟环境)
uv run src/my_project/main.py

# 4. 运行测试
uv run pytest

🧠 建议

  • 使用 src/ 目录结构可避免导入冲突;

  • .venv/ 加入 .gitignore

  • 使用 uv lock 确保依赖一致性。