Python 的包管理历史是一部混乱史。这篇文章梳理一下各种工具的演变,以及我现在的实践选择。
为什么需要虚拟环境
不同项目依赖不同版本的库。不用虚拟环境,所有包都装在系统 Python 里,版本冲突是迟早的事:
项目 A:需要 Django 3.2
项目 B:需要 Django 4.2
→ 只能装一个,两个项目不能同时开发
虚拟环境为每个项目创建独立的 Python 环境,依赖互不干扰。
各种工具的演变
venv(Python 3.3+ 内置)
python -m venv .venv
source .venv/bin/activate # macOS/Linux
.venv\Scripts\activate # Windows
pip install requests
pip freeze > requirements.txt
最基础,不需要额外安装,但功能简单,requirements.txt 不区分开发依赖和生产依赖。
virtualenv + pip-tools
pip install pip-tools
# requirements.in(只写直接依赖)
requests>=2.28
flask>=2.0
# 生成锁定版本的 requirements.txt
pip-compile requirements.in
# 安装
pip-sync requirements.txt
pip-compile 解决了依赖锁定的问题,是很长一段时间内的最佳实践。
Poetry
poetry new my-project
poetry add requests
poetry add pytest --group dev
poetry install
# pyproject.toml 管理所有配置
Poetry 把依赖管理、打包、发布整合在一起,pyproject.toml 是单一配置文件。问题是速度慢,解析依赖时间长。
uv(当前推荐)
uv 是 Astral(Ruff 的团队)用 Rust 写的,速度快了 10-100 倍:
# 安装
curl -LsSf https://astral.sh/uv/install.sh | sh
# 创建虚拟环境
uv venv
# 安装包
uv pip install requests
# 运行脚本(自动激活虚拟环境)
uv run python main.py
# 初始化项目(用 pyproject.toml)
uv init my-project
cd my-project
uv add requests
uv add pytest --dev
速度对比(安装 50 个包):
- pip:~30s
- poetry:~45s
- uv:~3s
我现在的工作流
# 新项目
uv init my-project
cd my-project
# 添加依赖
uv add fastapi uvicorn
uv add pytest httpx --dev
# 运行
uv run python main.py
uv run pytest
# 同步环境(从 pyproject.toml 安装所有依赖)
uv sync
uv.lock 文件锁定了所有依赖的精确版本,提交到 git 里,保证所有人环境一致。
一个建议
不管用什么工具,一定要用虚拟环境,一定要锁定依赖版本。“在我机器上能跑"是最常见的协作问题根源之一。