Python 虚拟环境:为什么不能直接 pip install
多项目并行开发时,最容易出现的状态是「A 项目要 1.2 版,B 项目要 2.0 版,装哪个另一个就崩」。 虚拟环境解决的就是这个问题,原理比想象的简单。
一、venv 建出来的到底是什么
它不是复制了一整份 Python 解释器,只是一个目录,里面放了几样东西:
pyvenv.cfg记录基础解释器的路径和版本。bin/python(Windows 是Scripts\python.exe)指向系统解释器的链接或小副本。lib/python3.x/site-packages/这个项目专属的第三方包目录,关键在这里。bin/activate一个 shell 脚本,作用就是把PATH和VIRTUAL_ENV改一下。
所以「激活」并不神奇——它没有改变 Python 本身,
只是让你敲 python / pip 时先找到这个目录里的可执行文件,
而解释器启动时会优先从同目录的 site-packages 里找包。
二、创建与激活
python3 -m venv .venv
# Linux / macOS
source .venv/bin/activate
# Windows PowerShell
.venv\Scripts\Activate.ps1
# Windows cmd
.venv\Scripts\activate.bat
# 退出
deactivate
.venv 这个目录名是约定俗成的,好处是主流编辑器和工具会默认去这里找解释器。
它不该进版本库——在 .gitignore 里加一行就够。
.venv/
__pycache__/
*.pyc
三、requirements.txt 的正确用法
pip install -r requirements.txt # 按文件装
pip freeze > requirements.txt # 把当前环境导出来
pip freeze 会导出所有已安装的包,包括依赖的依赖。
这在「复现一个已知能跑的环境」时很方便,但它有两个明显缺点:
- 把间接依赖也钉死了,别人装的时候会失去灵活升级的空间。
- 不记录哈希,也不区分「直接依赖」和「顺带装上的」。
更清楚的做法是手写只列直接依赖的 requirements.in,
再用 pip-tools 生成带完整约束的 requirements.txt;
或者用 pyproject.toml 描述依赖,由构建工具锁定。
# 一个手写的最小 requirements.txt
requests>=2.31,<3
flask~=3.0
# 注:~= 表示允许补丁位变化,即 3.0.x
四、镜像源
国内直连官方源经常超时,可以换成镜像:
# 临时用一次
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests
# 写进配置(推荐,一次配置长期有效)
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip config list # 检查当前生效的配置
配置写在 ~/.config/pip/pip.conf(Windows 是 %APPDATA%\pip\pip.ini),
只影响当前用户,不会污染系统。
五、别用 sudo pip
sudo pip install 会把包装进系统 Python,可能覆盖发行版自己依赖的版本,
后果是包管理器(apt/dnf)跟着出问题——而且当时完全看不出来。
需要装到用户目录时用:
pip install --user requests
六、externally-managed-environment 是什么
新版 Debian / Ubuntu 上直接 pip install 会报:
error: externally-managed-environment
× This environment is externally managed
这是 PEP 668 引入的保护:系统 Python 归包管理器管,pip 不许往里装东西。
正确反应是建虚拟环境,而不是加 --break-system-packages 把它关掉。
真要装成系统级命令,就打成包(pipx 是专门干这个的)。
七、几个小习惯
- 项目根目录放
.python-version,配合pyenv固定解释器版本,避免「我这能跑」。 - 装完依赖先
pip check,它会报出依赖冲突,比如 A 要urllib3<2而 B 要>=2。 - 升级单个包用
pip install -U 包名,但别不加思索地pip install -U全量升级,容易一次崩一串。
一句话总结:每个项目一个 venv,装依赖用 requirements.txt 记录,别用 sudo pip;遇到 externally-managed-environment 就该建环境,而不是加 --break-system-packages。