跳到主要内容

彻底解决Python环境管理混乱

· 阅读需 8 分钟
amass
一个躺不平的板砖人

说来有点丢人:我抗拒 Python 抗拒了很多年。倒不是嫌它语法,而是怕它把我电脑搞乱——装个 2.x 又装个 3.x,同一个库这个项目要这个版本、那个项目要那个版本,全局 pip install 装一堆东西,最后既不知道哪个 python 在生效,也不敢删,删了又怕牵连别的。作为一个搞嵌入式和 C++ 出身、主力 Windows 的人,这种“装不干净、卸不彻底”的不确定感,比写不出代码更让我难受。

后来认真捋了一遍才服气:这套顾虑在旧做法下确实成立,不是我矫情,但现代工具链早把它解决干净了。

乱从何来

根源就一句话:往全局/系统解释器里装东西

  • sudo pip install 或不开虚拟环境直接 pip install,包会写进全局 site-packages。在 Linux 上尤其危险——系统脚本(apt/dnf)依赖特定版本的库,你顶替过去可能破坏系统工具且难以恢复,而 pip uninstall 又可能删掉发行版“拥有”的文件。
  • 多个安装器混装(python.org 版、Microsoft Store 版、各种 launcher),PATH 顺序一乱,python 指向谁全凭运气。
  • pip freeze > requirements.txt 只是快照当前已装内容、不做依赖求解,没有原生 lockfile,传递依赖照样漂移。

现代 Linux 发行版其实已经在自卫了,这就是 PEP 668:它在 stdlib 路径放一个 EXTERNALLY-MANAGED 标记,pip 检测到就拒绝往全局装,报 externally-managed-environment。这个报错不是 bug,是在提醒你别动系统解释器;想强行绕过得用 --break-system-packages,名字起得就很吓人。

核心原则

把这三条刻进去,剩下的都是工具选择:

  1. 永不污染全局/系统解释器。 系统自带的 Python 是给系统用的。
  2. 每个项目一个自包含、可整目录删除的隔离环境。 删目录 = 彻底卸载,不触碰任何全局状态。
  3. 用 lockfile 保证可复现地重装。 锁定的不只是直接依赖,还有整个传递依赖闭包。

满足这三条,“卸得干净、装得回来”就是必然结果,而不是运气。

推荐:uv 一站式

2026 年我的主线答案是 uv。它是 Astral(ruff 那个团队)用 Rust 写的一体化工具,把 pyenv(多版本解释器)、venv(隔离环境)、pip/pip-tools(依赖与锁定)、pipx(全局工具)全包了,而且显著快于 pip(靠全局缓存 + 硬链接复用,具体倍数自查)。

它正好对上前面三条原则:装的是独立构建的“托管 Python”(python-build-standalone),不接管也不依赖系统 Python;每个项目自动管理 .venvuv.lock 是带哈希的跨平台精确锁文件。

日常工作流就这么几条,跨平台一致:

uv python install 3.12 # 下载并管理一个托管解释器,不碰系统 Python
uv init my-project # 生成 pyproject.toml
uv add requests # 增依赖,自动更新 pyproject.toml 与 uv.lock
uv run main.py # 在项目环境里跑,运行前自动确保依赖与解释器就绪
uv sync # 让环境严格对齐 uv.lock

分工记清楚:pyproject.toml 是人改的声明式依赖,uv.lock 是机器生成的精确锁文件(含哈希,要提交进 Git,别手改)。换台机器 git cloneuv sync,环境一模一样还原——这就是“装得回来”。安装方式(PowerShell 的 irm ... | iex、winget、或 macOS/Linux 的 curl ... | sh)和更多命令直接查官方文档,这里不铺开。

要彻底卸载,记住两件事就够:uv 的数据全在用户级目录、不需要 root(缓存在 %LOCALAPPDATA%\uv\cache,托管 Python、工具、凭据等持久化数据在 %APPDATA%\uv\data 下——注意缓存与持久化数据分属两个不同的根);卸载顺序是先用 uv cache dir / uv python dir / uv tool dir 把路径问出来并删干净,最后才删 .local\bin 里的二进制。顺序反了就没法再用 uv 查路径,这是最常见的坑。包管理器装的(winget/brew/scoop)先走对应渠道卸二进制;最后别忘了清掉安装脚本写进 shell 配置/PATH 的那行。

注意

uv self update 只对独立安装器装的 uv 有效,包管理器装的走各自渠道。uv.lock 是 uv 专有格式,喂不了 pip/poetry,需要时用 uv export 导出 requirements.txt

其它路线与定位

uv 是主线,但不是唯一解。按场景一句话定位:

  • pyenv + venv 经典栈。 想理解底层、或维护遗留项目时用。pyenv 管多解释器版本(靠 PATH 最前的 shim 拦截 python),python -m venv .venv 建项目级隔离环境,二者正交互补、都不动系统 Python。缺点是 requirements.txt 的可复现有限,要真锁闭包还得再上 pip-tools / Poetry / uv。
  • conda / Miniforge。 当你要的不是纯 Python 包,而是 CUDA toolkit、cuDNN、GDAL、HDF5、R 这类非 Python 二进制依赖时,这才是 conda 不可替代的理由——uv/venv 装不了这些。新装直接用 Miniforge(社区维护、默认只连 conda-forge、规避 Anaconda 商业许可红线,mamba 已内置)。
  • pipx(或 uv tool / uvx)。全局 CLI 工具(black、ruff、httpie、cookiecutter)用的:每个工具单独一个 venv,互不打架。它面向“应用”,不是给你项目 import 的库。
  • Docker / Dev Containers。 OS 级终极隔离,宿主机零污染;团队/多机一致性用 .devcontainer/devcontainer.json 把环境写成代码。镜像选 slim 别选 alpine(musl libc 与二进制 wheel 兼容性差)。
注意

conda 与 pip 混用顺序敏感:先 conda 装全,最后才 pip 补;一旦 pip 介入就别再回头 conda install。永远别往 base 环境堆项目依赖,按项目建独立环境。

卸载与重装:就一句

各方案“删哪个、怎么回来”其实可以收敛成一句:隔离环境都是可丢弃目录,删目录就是卸载;有了 lockfile/声明文件,重装就是一条命令(uv 用 uv sync,venv 从 requirements.txt 重建,conda 用 environment.yml,Docker 用 docker build)。pyenv-win 这类则是删 %USERPROFILE%\.pyenv 整个目录、再清掉 PATH 里 %USERPROFILE%\.pyenv\pyenv-win\bin%USERPROFILE%\.pyenv\pyenv-win\shims 两条。这正是旧做法做不到、新做法能做到的地方。

唯一例外是已经被污染的全局/系统 Python:逐个 pip uninstall 风险高(可能删到发行版文件)。这种别折腾清理了,以后一律走 venv/uv,把系统 Python 留给系统。

Windows 上的坑

我主力 Windows,这里踩得最多。最值钱的一条判断是:系统里常同时存在 python.org 版、Microsoft Store 版,以及 WindowsApps 下的应用执行别名(App Execution Aliases)那个伪 python.exe。别名在 PATH 靠前时会拦截命令,没装时还会把你弹去微软商店(经典的 “Python was not found”)。所以 where.exe 列的命中顺序不等于真相,只有 sys.executable 才是真正在跑的解释器

where.exe python # PATH 中所有命中及顺序
Get-Command python # 实际解析到的 Source
python -c "import sys; print(sys.executable)" # 真正运行的解释器绝对路径

排查就这三件套,选解释器优先用 py launcher(py --list-paths 看已注册版本、py -3.12 ... 指定)。多余的 Python 与别名到「设置」里正规卸载/关闭即可,注册表卸载项别手动碰。

PowerShell 还有个常见拦路虎:跑 venv 的 Activate.ps1 报“未签名/禁止运行脚本”。Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser 即可(只影响当前用户、无需管理员)。


捋完这一圈,我对 Python 的抵触基本没了——问题从来不在 Python 本身,而在旧做法默认往全局装。现在我新项目一律 uv,重二进制/CUDA 场景才上 Miniforge,全局小工具走 uvx。想再深入可以接着搜:uv 的 workspace 多包管理、pixi(prefix.dev 出的、兼顾 conda 生态与 uv 速度的新工具)、以及 --require-hashes 做完整性校验。

评论

欢迎补充上下文、指出问题,或者留下进一步讨论的线索。

正在加载留言板…