Python 虚拟环境与依赖锁定:六个常见坑
先说在前面
「在我机器上跑得好好的」十次里有八次是依赖问题:Python 版本不同、包装在全局环境、requirements.txt 没锁版本、或者某个包只在 Windows 上装得到。这些问题不会在写代码时暴露,往往在 CI、部署或换同事接手时才炸。下面六个按出现频率排,每条给一条能立刻验证的命令。
1. 忘了激活虚拟环境,包装到了全局
最常见的开头:pip install requests 装进了系统 Python,脚本却用另一个解释器跑,于是 ModuleNotFoundError 和「明明装过了」同时出现。先确认当前到底在用哪个 Python、哪个 pip:
which python3; python3 -c "import sys; print(sys.executable)"
python3 -m pip --version
# 更稳的习惯:永远用解释器带 pip
python3 -m pip install requests
python3 -m venv .venv 建好环境后要 source .venv/bin/activate(Windows 是 .venv\Scripts\activate)。IDE 里还要注意解释器路径有没有指向 .venv,终端激活了但 IDE 没切,照样会踩坑。
2. requirements.txt 只写包名,不锁版本
手写 requests、pandas 而不带版本号,等于把构建结果交给「安装当天 PyPI 上是什么版本」。上游一发不兼容更新,CI 就红,而且本地复现不出来。
# 最低限度:写精确版本
requests==2.32.3
pandas==2.2.3
# 更规范:交给工具生成锁文件
pip install pip-tools
pip-compile requirements.in -o requirements.txt
pip-sync requirements.txt
requirements.in 只放直接依赖(人维护),requirements.txt 由 pip-compile 生成(机器维护,含完整传递依赖和精确版本)。别手改生成的文件。
3. 直接 pip freeze 把整棵树倒出来
pip freeze > requirements.txt 好用但有三个副作用:把传递依赖和直接依赖混在一起、带上本地才有的包(比如某个调试工具)、把平台专属包(pywin32、nvidia-*)一起写进去,换到 Linux 就装不上。
# 平台标记写法,让依赖只在对应环境生效
pywin32==306; sys_platform == "win32"
# 删除不在锁文件里的包,保证环境和锁文件一致
pip-sync requirements.txt
另外 pip freeze 会记录本地可编辑安装(-e .)的路径,别把这种绝对路径提交到仓库。
4. .venv 和 pycache 被提交进仓库
虚拟环境目录几千个文件,一旦进 Git 就很难清干净,还会把某个同事机器上的绝对路径带进历史。新建项目第一件事就是写 .gitignore:
.venv/
venv/
__pycache__/
*.py[cod]
.pytest_cache/
*.egg-info/
.env
已经提交过的目录,加忽略规则是没用的(.gitignore 只作用于未跟踪文件),要 git rm -r --cached .venv 再提交一次。
5. 用 sudo pip 或污染系统 Python
Linux 上 sudo pip install 会把包写进发行版自带的 Python,可能覆盖系统工具依赖的包,甚至把 pip 自己搞坏。macOS/Homebrew 的 Python 还有 externally-managed-environment 保护,直接装会报 error: externally-managed-environment。
# 不要 sudo pip install
python3 -m venv .venv && source .venv/bin/activate
python -m pip install -U pip
# 命令行工具用隔离安装
pipx install black
系统 Python 只用来跑系统脚本,业务依赖一律放虚拟环境;命令行小工具交给 pipx,互不干扰。
6. 镜像源与私有源混用、缓存没清
团队里有人配了公司镜像、有人用默认 PyPI,同一个锁文件可能装出不同的包(尤其是内部包和同名包)。CI 里还要留意缓存目录没随锁文件更新,导致「改了版本却还在用旧 wheel」。
pip config list
pip cache dir; pip cache purge
pip install --no-cache-dir -r requirements.txt
# CI 里让缓存键包含锁文件哈希
# key: pip-${{ hashFiles('requirements.txt') }}
建议在项目里放一份 pip.conf 或在 CI 显式指定 --index-url,把源固定下来,别依赖每个开发者的本机配置。
小结:一条能直接照抄的流程
python3 -m venv .venv → 激活并确认 sys.executable → 写 .gitignore → 只把直接依赖写进 requirements.in → pip-compile 生成锁文件 → pip-sync 安装 → CI 用同一份锁文件 + 固定镜像源 + 带哈希的缓存键。现在也可以用 uv 把这几步压到两条命令(uv venv、uv pip sync),但背后要解决的还是这六件事。把「环境是可重建的」当成默认前提,绝大多数「在我机器上是好的」就消失了。
- 点赞
- 收藏
- 关注作者
评论(0)