跳到主要内容

12. 虚拟环境与包管理

对应官方文档:12. Virtual Environments and Packages 一句话导读:这一章解决「一台电脑上同时做好几个项目,装包怎么不打架」——用 venv 给每个项目隔出一间干净的小屋,再用 pip 管好依赖清单,学完能把环境连同代码一起交给别人复现。

前面十一章都在写「一个 .py 文件里的事」,真实项目绕不开第三方包。只要开始 pip install,麻烦就来了:不同项目要同一个包的不同版本、系统自带的 Python 被装脏、换台电脑跑不起来。官方第 12 章就是来解决这个的,村长按 12.1 到 12.3 的编号走,命令全部给全,macOS、Linux、Windows 都照顾到。

12.1 引言​

先讲清楚「为什么」。Python 默认把第三方包装进一处公共目录(site-packages)。这带来一个躲不掉的冲突:

项目依赖后果
老项目 Arequests==2.28.0装了新版就跑不起来
新项目 Brequests==2.31.0装了旧版用不了新特性
同一台机器只能留一个版本两个项目必坏一个

这就是所谓的依赖冲突:不光是同一个包的不同版本,A 依赖的 numpy<2 和 B 依赖的 numpy>=2 也会互相打架。更要命的是,很多系统的包管理器(macOS 的 Homebrew Python、Linux 发行版的 /usr/bin/python3)自己也依赖某些 Python 包,你 sudo pip install 一通乱装,可能把系统工具搞坏。

虚拟环境的思路特别朴素:不再共用那一个 site-packages,而是给每个项目单独开一间「小屋」——里面有自己的 Python 解释器入口、自己的 pip、自己专属的 site-packages。在项目 A 的小屋里装 2.28,在项目 B 的小屋里装 2.31,两边井水不犯河水。

你可以这样理解:系统 Python 是整栋大楼的公共仓库,虚拟环境是给每户人家配的独立储物间。储物间里放什么、放多满,都不影响楼里其他住户,也不用大楼管理员(管理员权限)批准。

虚拟环境还有两个附带好处:

  • 可复现:把「我这间屋里装了哪些包、各是什么版本」导出成 requirements.txt,别人照单抓药就能得到一模一样的环境。
  • 可丢弃:项目不做了,rm -rf .venv 一删了事,系统 Python 干干净净,完全不用回滚。

村长划重点:虚拟环境不是把整个 Python 复制一份(那样太占地方),它本质是几个符号链接 + 一个独立的 site-packages + 一份 pyvenv.cfg 配置。所以创建很快、体积很小,也不该把它提交到 Git——环境是能随时重建的「产物」,代码才是要版本管理的「源文件」。

12.2 创建虚拟环境​

标准库自带 venv,不用额外装任何东西。习惯上把环境建在项目根目录里、取名 .venv(前面带点,默认隐藏)。

# 1. 建项目目录并进入
mkdir myproject && cd myproject

# 2. 创建虚拟环境(macOS / Linux 用 python3,Windows 用 python 或 py)
python3 -m venv .venv
# Windows 也可以写成:
# py -m venv .venv

python -m venv 的意思是「把 venv 这个标准库模块当脚本跑」,.venv 是目标目录名,可以换,但强烈建议就用 .venv,各种编辑器和工具都默认认它。

激活环境​

创建完还不能用,得激活——本质是把 .venv/bin(Windows 是 .venv\Scripts)临时插到 PATH 最前面,让 python、pip 这两个命令指向小屋里的副本。

# macOS / Linux(bash 或 zsh)
source .venv/bin/activate
# Windows PowerShell
.venv\Scripts\Activate.ps1
# Windows CMD(命令提示符)
.venv\Scripts\activate.bat

激活成功后,命令行提示符前面会多出环境名,这是判断「我现在到底在哪间屋」最直观的办法:

$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $ which python
/Users/jay/myproject/.venv/bin/python
(.venv) $ pip --version
pip 24.x from /Users/jay/myproject/.venv/lib/python3.12/site-packages (python 3.12)

PowerShell 里若提示「禁止运行脚本」,先执行一次 Set-ExecutionPolicy -Scope CurrentUser RemoteSigned 再激活即可,这是 Windows 的执行策略限制,不是 Python 的问题。

不想猜,也可以让 Python 自己报告「我是不是虚拟环境」:

import sys
print('解释器路径:', sys.executable)
print('sys.prefix:', sys.prefix)
print('基础解释器前缀:', sys.base_prefix)
print('当前在虚拟环境里吗:', sys.prefix != sys.base_prefix)

目录结构​

建好之后大致长这样(版本号随你的 Python 变):

myproject/
├── .venv/
│ ├── bin/ # macOS / Linux
│ │ ├── activate # source 的就是它
│ │ ├── activate.csh
│ │ ├── Activate.ps1
│ │ ├── python -> python3 # 指向基础解释器的符号链接
│ │ └── pip
│ ├── Scripts/ # Windows 用这个目录,不放 bin/
│ │ ├── activate.bat
│ │ ├── Activate.ps1
│ │ ├── python.exe
│ │ └── pip.exe
│ ├── lib/
│ │ └── python3.12/
│ │ └── site-packages/ # ← 本项目专属的第三方包都在这
│ ├── include/
│ └── pyvenv.cfg # 记录基础解释器在哪、版本多少
└── your_code.py

pyvenv.cfg 是个几行的小文本,内容类似:

home = /Library/Frameworks/Python.framework/Versions/3.12/bin
implementation = CPython
version_info = 3.12.4
virtualenv = true
include-system-site-packages = false

include-system-site-packages = false 表示「默认看不见系统装过的包」,这正是隔离的关键。想让环境复用系统已装的包,创建时加 --system-site-packages;反过来 --without-pip 可以建一个不带 pip 的极简环境。

用完了退出环境:

deactivate # 各平台通用,回到系统 Python

最后,建完环境第一件事是把它排除出版本库:

printf '.venv/\n__pycache__/\n' > .gitignore

村长划重点:每次新开一个终端都要重新 source .venv/bin/activate,激活只对当前这个终端窗口有效,重开就失效了。判断自己有没有在环境里,看提示符有没有 (.venv),或者跑 which python / where python 看路径是不是指向项目目录——装错到系统 Python 是新手最常犯、也最难查的错。

12.3 用 pip 管理包​

pip 是 Python 的包管理器,虚拟环境里自带。村长先给一个贯穿全节的建议:写 python -m pip 而不是裸 pip。因为裸 pip 到底对应哪个解释器取决于 PATH,一旦忘激活就装到系统里去了;python -m pip 则明确用「当前这个 python」的 pip,永远和 sys.executable 一致。

# 先把 pip 自己升到较新版本(可选,但推荐)
python -m pip install --upgrade pip

# 安装包
python -m pip install requests

# 指定精确版本 / 版本范围(shell 里用引号包住比较运算符)
python -m pip install 'requests==2.31.0'
python -m pip install 'requests>=2.28,<3'

# 升级到最新版
python -m pip install --upgrade requests

# 卸载
python -m pip uninstall requests

# 看装了什么 / 看某个包的详情
python -m pip list
python -m pip show requests

== 锁死版本,>=、<、~= 写范围;引号不能省,否则 shell 会把 > 当重定向符,命令直接跑偏。

用 requirements.txt 复现环境​

pip freeze 把当前环境里所有包连同精确版本打印出来,重定向进文件就是一份依赖清单:

python -m pip freeze > requirements.txt

生成的文件是纯文本,一行一个包:

certifi==2024.8.30
charset-normalizer==3.4.0
idna==3.10
requests==2.31.0
urllib3==2.2.3

队友拿到代码后,只要建好空环境、照单安装,就能得到一致的版本:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt

freeze 会把间接依赖(比如 requests 顺带装进来的 urllib3)也全列上,好处是复现精确到每一个包;若你只想声明「我直接用到的顶层依赖」,也可以手写一份精简的 requirements.txt,让 pip 自己去算依赖树。两种写法团队里定一种就行。

pip 命令速查​

想做的事命令
装包python -m pip install 包名
装指定版本python -m pip install '包名==1.2.3'
升级包python -m pip install --upgrade 包名
卸载包python -m pip uninstall 包名
列已装python -m pip list
导出清单python -m pip freeze > requirements.txt
按清单安装python -m pip install -r requirements.txt
查包详情python -m pip show 包名
检查依赖冲突python -m pip check

关于 --user​

pip install --user 包名 会把包装到当前用户目录(macOS/Linux 是 ~/.local,Windows 是 %APPDATA%\Python),不碰系统目录、不需要管理员权限。它是没有虚拟环境年代的折中办法:一台机器一个用户一份包,能绕开权限问题,但仍然解决不了 12.1 的版本冲突——因为同一个用户的所有项目还是共用那一处。

在已激活的虚拟环境里 --user 是被禁止的(Python 会直接报错),因为环境本身就是隔离的,再写用户目录只会制造「包到底装哪了」的混乱。现代做法很干脆:一律用虚拟环境,--user 基本用不上。顺带一提,近几年 Linux 发行版开始启用 PEP 668「外部管理环境保护」,直接 pip install 会被拦下并提示你先建虚拟环境——这不是出错,是系统在保护自己。

村长划重点:requirements.txt 要跟着代码一起提交,.venv/ 绝对不要提交,这俩别搞反。另一个坑是别混用 pip 和 python -m pip:先 pip install 再 python -m pip list 发现「包不见了」,八成是两条命令落到了不同解释器上。统一用 python -m pip,从此没有这种灵异事件。

完整实战流程​

把整章串一遍,从零到能交给队友复现,就是这个样子:

# ① 建项目 + 建环境
mkdir myproject && cd myproject
python3 -m venv .venv

# ② 激活(Windows PowerShell 换成 .venv\Scripts\Activate.ps1)
source .venv/bin/activate

# ③ 确认装的是「这间屋」的 Python
which python
python -c "import sys; print(sys.executable)"

# ④ 装依赖、写代码、跑起来
python -m pip install 'requests==2.31.0'
# ⑤ 导出清单并排除环境目录
python -m pip freeze > requirements.txt
printf '.venv/\n__pycache__/\n' > .gitignore

# ⑥ 收工时退出
deactivate

# ---- 队友那边 ----
# python3 -m venv .venv
# source .venv/bin/activate
# python -m pip install -r requirements.txt

本章小结​

  • 依赖冲突来自「所有项目共用一个 site-packages」;虚拟环境给每个项目独立的解释器入口与 site-packages,顺带实现可复现、可丢弃。
  • 用 python -m venv .venv 创建;macOS/Linux 用 source .venv/bin/activate,Windows PowerShell 用 .venv\Scripts\Activate.ps1、CMD 用 activate.bat;deactivate 退出。
  • 环境靠 pyvenv.cfg 记录基础解释器、site-packages 存放专属包;.venv/ 必须写进 .gitignore,别提交。
  • 一律用 python -m pip:install / --upgrade / uninstall / list / show / freeze > requirements.txt / install -r requirements.txt 是核心六件套。
  • 版本用 ==、>=、< 指定并加引号;--user 是旧办法且在虚拟环境里被禁用,PEP 668 的保护提示不是报错。