5 minutes
虚拟环境与依赖管理
想象一下:你在项目 A 里用 Django 4.2,项目 B 因为遗留代码只能用 Django 3.2。如果全局只装一个版本的 Django,你只能在两个项目之间反复卸载重装——这简直是噩梦。
虚拟环境(Virtual Environment)就是来解决这个问题的:它为每个项目创建独立的 Python 环境,每个环境有自己的一套包,互不干扰。
为什么需要虚拟环境?
依赖冲突问题
没有虚拟环境时,你的全局 Python 环境是这样的:
# 全局安装的包(所有项目共享)
pip list
# Django 4.2
# requests 2.31
# numpy 1.26
这对不同项目的需求造成了冲突:
| 项目 | 需要的版本 | 问题 |
|---|---|---|
| 项目 A | Django 4.2 | ✅ |
| 项目 B | Django 3.2 | ❌ 全局装了 4.2 |
| 项目 C | requests 2.20 | ❌ 全局装了 2.31 |
虚拟环境的解决方案
系统 Python
├── 虚拟环境 A → Django 4.2, requests 2.31
├── 虚拟环境 B → Django 3.2, requests 2.20
├── 虚拟环境 C → numpy 1.26, pandas 2.0
└── 全局环境 → pip, setuptools, wheel(基础工具)
每个虚拟环境是独立的目录,包含自己的 python 解释器副本和 site-packages 目录。包安装到虚拟环境中不影响系统或其他环境。
使用 venv(内置方案)
Python 3.3+ 内置了 venv 模块,无需额外安装。
创建虚拟环境
# 创建虚拟环境(会在当前目录创建 venv 文件夹)
python -m venv venv
# 或者指定名称
python -m venv .venv
通常约定使用 venv 或 .venv 作为虚拟环境目录名,它会被添加到 .gitignore 中。
激活虚拟环境
# Windows(CMD)
venv\Scripts\activate
# Windows(PowerShell)
venv\Scripts\Activate.ps1
# macOS / Linux
source venv/bin/activate
激活后,命令行提示符会显示环境名称:
(venv) C:\Users\me\project>
此时 python 和 pip 都会指向虚拟环境中的版本。
停用虚拟环境
deactivate
安装包
# 激活环境后
pip install requests
pip install django==4.2
pip install "numpy>=1.26,<2.0"
删除虚拟环境
# 直接删除目录即可
rm -rf venv # macOS/Linux
rmdir /s venv # Windows
然后重新创建——一切从零开始。
requirements.txt 锁定依赖
当项目需要共享时,需要记录所有依赖:
# 将所有依赖导出到文件
pip freeze > requirements.txt
生成的 requirements.txt 内容:
certifi==2024.7.4
charset-normalizer==3.3.2
django==4.2.16
idna==3.7
requests==2.31.0
sqlparse==0.5.1
urllib3==2.2.2
其他开发者拿到项目后:
# 1. 创建并激活虚拟环境
python -m venv venv
source venv/bin/activate # macOS/Linux
# venv\Scripts\activate # Windows
# 2. 一键安装所有依赖
pip install -r requirements.txt
requirements.txt 的最佳实践
可以按照用途分类多个文件:
# requirements.txt(生产依赖)
django>=4.2,<5.0
requests>=2.31
gunicorn>=22.0
# requirements-dev.txt(开发依赖)
-r requirements.txt # 包含生产依赖
pytest>=8.0
pytest-cov>=5.0
black>=24.0
ruff>=0.5.0
mypy>=1.10
# 安装所有开发依赖
pip install -r requirements-dev.txt
依赖全量固定的最佳实践
使用 pip freeze 导出版本锁定的文件,与手写的宽松版本分开:
# requirements.in(手写,宽松版本)
django
requests
psycopg2-binary
# requirements.txt(自动生成,精确锁定)
# 用 pip freeze 或 pip-compile 生成
pip-tools:依赖版本锁定
pip-tools 帮你管理依赖的依赖——它能解析所有传递依赖,并锁定精确版本:
pip install pip-tools
# 1. 手写 requirements.in(只写直接依赖)
echo "django>=4.2" > requirements.in
echo "requests" >> requirements.in
# 2. pip-compile 解析所有依赖,生成精确锁定的 requirements.txt
pip-compile requirements.in
# 3. 用生成的 requirements.txt 安装
pip-sync requirements.txt
pip-compile 生成的 requirements.txt 包含了所有传递依赖的精确版本:
# This file is autogenerated by pip-compile with Python 3.13
# by the following command:
# pip-compile requirements.in
#
asgiref==3.8.1
# via django
certifi==2024.7.4
# via requests
charset-normalizer==3.3.2
# via requests
django==5.1.1
# via -r requirements.in
idna==3.7
# via requests
requests==2.31.0
# via -r requirements.in
urllib3==2.2.2
# via requests
pip-sync 不仅安装新包,还会卸载环境中不在 requirements.txt 里的包,确保环境完全同步。
Poetry:现代化包管理
Poetry 是一个集依赖管理、打包、发布于一体的工具。
安装 Poetry
# 推荐方式
pipx install poetry
# 或用官方安装脚本
curl -sSL https://install.python-poetry.org | python3 -
创建新项目
# 创建新项目
poetry new myproject
# 在已有项目中初始化
poetry init
pyproject.toml
Poetry 使用 pyproject.toml 替代 requirements.txt:
[project]
name = "myproject"
version = "0.1.0"
description = "一个示例项目"
requires-python = ">=3.10"
dependencies = [
"django>=4.2,<5.0",
"requests>=2.31",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"black>=24.0",
]
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Poetry 的核心命令
# 安装所有依赖
poetry install
# 只安装生产依赖
poetry install --only main
# 添加依赖
poetry add requests
poetry add --group dev pytest
# 移除依赖
poetry remove requests
# 更新依赖到最新版本(在版本约束范围内)
poetry update
# 显示依赖树
poetry show --tree
# 生成 requirements.txt(给不使用 Poetry 的人)
poetry export -f requirements.txt --output requirements.txt
虚拟环境的自动管理
Poetry 默认自动创建和管理虚拟环境:
# 查看当前使用的虚拟环境信息
poetry env info
# 手动指定 Python 版本创建虚拟环境
poetry env use python3.13
# 列出项目的所有虚拟环境
poetry env list
# 删除虚拟环境
poetry env remove --all
uv:极速替代方案
uv 是使用 Rust 编写的 Python 包管理工具,速度比 pip 快 10-100 倍:
# 安装 uv
pip install uv
# uv 可以作为 pip 的直接替代品
uv pip install requests
# 创建虚拟环境
uv venv
# 从 requirements.txt 安装
uv pip install -r requirements.txt
# 编译依赖
uv pip compile requirements.in -o requirements.txt
# 同步环境
uv pip sync requirements.txt
uv 的特点:
- 速度极快:Rust 实现,比 pip 快 10-100 倍
- 完全兼容:语法和 pip 几乎一样,迁移成本极低
- 全局缓存:跨项目共享包缓存,节省磁盘空间
- 无锁竞争:多进程安装互不阻塞
# 用 uv 初始化项目
uv init myproject
cd myproject
# 添加依赖
uv add requests
uv add --dev pytest
# 运行命令
uv run python -m pytest
Conda:数据科学的首选
Conda 是 Anaconda 生态的包管理工具,主要面向数据科学和机器学习。
# 创建环境
conda create -n myenv python=3.13
# 激活环境
conda activate myenv
# 安装包(Conda 仓库)
conda install numpy pandas scikit-learn
# 安装包(PyPI)
pip install some-package
# 导出环境
conda env export > environment.yml
# 从文件创建环境
conda env create -f environment.yml
Conda 的特点:
- 跨语言:不仅管理 Python 包,还能管理 C/C++ 库、R 包等
- 二进制包:预编译的二进制文件,不需要编译工具链
- 数据科学生态:numpy、pandas、scikit-learn 等的安装体验极佳
但 Conda 的缺点也很明显:仓库更新比 PyPI 慢,且环境解析速度较慢。
依赖解析问题
什么是依赖地狱?
项目要求:
├── package-a==1.0
│ └── requires: package-c>=2.0
├── package-b==2.0
│ └── requires: package-c<2.0
└── → package-c 无法满足两个约束!
这就是依赖冲突——两个包对同一个传递依赖有互不兼容的版本要求。
pip vs Poetry 的依赖解析
# pip 采用贪心解析——可能安装出有冲突的版本
pip install package-a package-b
# 成功安装了……但运行时报错
# Poetry 采用 SAT 求解器——安装前就检测冲突
poetry add package-a package-b
# 如果冲突,直接报错并告诉你原因
解决依赖冲突的策略
- 升级/降级:看看有没有其他版本能兼容所有约束
- 更换替代包:如果 package-a 长期不更新,考虑找替代者
- 解耦:将两个不兼容的功能拆到不同的项目中
- 使用容器:Docker 容器各自有独立的环境
虚拟环境的最佳实践
1. 永远使用虚拟环境
# 不要这样做
pip install django # 全局安装!污染系统环境
# 应该这样做
python -m venv venv
source venv/bin/activate
pip install django # 在虚拟环境中安装
2. .gitignore 中加入虚拟环境目录
# .gitignore
venv/
.venv/
__pycache__/
*.pyc
.env
不要将虚拟环境提交到 Git——它是机器相关的,每个人应该自己创建。
3. 精确锁定版本并提交
# 生成锁文件并提交到 Git
pip freeze > requirements.txt
git add requirements.txt
这样所有人都能安装完全相同的版本,避免"在我机器上能跑"的尴尬。
4. 开发与生产依赖分离
# 安装生产环境依赖(线上服务器)
pip install -r requirements.txt
# 安装开发环境依赖(本地开发)
pip install -r requirements-dev.txt
5. 定期更新依赖
# 检查过期的包
pip list --outdated
# 更新单个包
pip install --upgrade requests
# 用 pur 批量更新(需要 pip install pur)
pur -r requirements.txt
6. 使用 pipx 隔离命令行工具
pipx 将 Python 命令行工具安装在隔离环境中,避免干扰项目依赖:
pip install pipx
# 安装工具——每个工具在自己的虚拟环境中
pipx install black
pipx install poetry
pipx install ruff
pipx install pytest
# 运行但不永久安装
pipx run cowsay "Hello"
# 列出已安装的工具
pipx list
实战:用 Poetry 构建项目
让我们完整地走一遍 Poetry 的项目创建流程:
# 1. 安装 Poetry
pipx install poetry
# 2. 创建新项目
poetry new task-cli
cd task-cli
生成的项目结构:
task-cli/
├── pyproject.toml
├── README.md
├── src/
│ └── task_cli/
│ └── __init__.py
└── tests/
├── __init__.py
└── test_task_cli.py
# 3. 添加依赖
poetry add click
poetry add --group dev pytest pytest-cov black ruff
# 4. 激活虚拟环境
poetry shell
# 5. 运行测试
poetry run pytest
# 6. 查看依赖树
poetry show --tree
生成的 pyproject.toml:
[project]
name = "task-cli"
version = "0.1.0"
description = "一个简单的任务管理 CLI 工具"
requires-python = ">=3.10"
dependencies = [
"click>=8.1",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"pytest-cov>=5.0",
"black>=24.0",
"ruff>=0.5.0",
]
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.black]
line-length = 88
[tool.ruff]
line-length = 88
target-version = "py310"
[tool.pytest.ini_options]
testpaths = ["tests"]
工具选型指南
| 场景 | 推荐工具 | 理由 |
|---|---|---|
| 标准库方案 | venv + pip |
零依赖,适合简单项目 |
| 需要版本锁定 | pip-tools |
传递依赖精确锁定 |
| 新项目/库开发 | Poetry | 一站式方案,兼顾依赖和打包 |
| 追求速度 | uv | Rust 实现,极速体验 |
| 数据科学/ML | Conda | 预编译二进制,跨语言支持 |
| 安装 CLI 工具 | pipx | 隔离命令行工具 |
小结
本章我们学习了 Python 虚拟环境与依赖管理的核心知识:
- 虚拟环境的意义:隔离项目依赖、避免版本冲突、保持系统整洁
- venv:Python 内置,
python -m venv venv创建,activate激活 - requirements.txt:
pip freeze导出,pip install -r安装,开发/生产分离 - pip-tools:
pip-compile解析依赖树,pip-sync同步环境 - Poetry:
pyproject.toml一站式管理,自动虚拟环境,SAT 求解器解析依赖 - uv:Rust 编写的极速 pip 替代品,完全兼容 pip 语法
- Conda:数据科学场景的首选,跨语言包管理
- 最佳实践:始终使用虚拟环境,锁定版本并提交,开发/生产分离,定期更新
记住:虚拟环境不是可选项,是每一个 Python 项目的必需品。无论项目大小,先创建虚拟环境再装包——这个习惯会让你的开发之路顺畅很多。
Summary: 虚拟环境、venv、pip-tools、Poetry、uv 与依赖管理实践。