
005、虚拟环境完全指南venv、virtualenv、conda 的原理与选择一个让我熬夜到凌晨3点的依赖冲突去年接手一个老项目Django 1.11 Python 2.7跑在CentOS 6上。我本地是Python 3.9pip install -r requirements.txt 直接炸了——Django版本不兼容接着numpy报错然后pandas说找不到某个C扩展。更离谱的是我同时还在开发一个新项目需要Python 3.10和最新的FastAPI。两个项目互相打架pip list一看全局site-packages里混着十几个版本的包谁是谁的依赖都分不清。那天晚上我干了件蠢事手动删了/usr/lib/python3.9/site-packages里一半的文件夹然后系统级的yum包管理器罢工了。第二天重装了系统。这就是虚拟环境要解决的问题。别像我一样用血泪换教训。venvPython官方给的“够用”方案Python 3.3开始内置了venv模块3.5之后成为推荐方式。它的原理很简单创建一个独立的目录里面放一个Python解释器的软链接外加一个独立的site-packages目录。python3-mvenv myproject_envsourcemyproject_env/bin/activate# Linux/Macmyproject_env\Scripts\activate# Windows激活后which python指向的是虚拟环境里的解释器pip install安装的包全放在myproject_env/lib/python3.x/site-packages/里。退出用deactivate。原理层面venv本质上修改了sys.path。激活脚本把虚拟环境的bin目录加到PATH最前面同时设置VIRTUAL_ENV环境变量。Python启动时会检查这个变量优先加载虚拟环境里的包。坑点不能移动虚拟环境目录。一旦移动所有软链接路径都失效。我见过同事把项目从/home/user/project移到/data/project然后虚拟环境直接报废。跨Python版本不兼容。venv只能基于创建时指定的Python版本不能切换。你没法用Python 3.9的venv跑Python 3.10的代码。系统级包污染。如果创建时用了--system-site-packages虚拟环境会继承全局包。这通常是个坏主意——你本来就是为了隔离结果又混进去了。什么时候用venv单项目、单Python版本、不需要频繁切换环境、团队里所有人都用Python 3.6。简单场景下venv够用别过度设计。virtualenvvenv的“亲爹”和增强版virtualenv比venv出现得更早支持Python 2.7到3.x。venv其实是virtualenv的一个子集实现但virtualenv功能更丰富。pipinstallvirtualenv virtualenv myenv--pythonpython3.8# 指定Python版本sourcemyenv/bin/activate核心差异支持指定任意Python解释器路径。你可以用--python/usr/local/bin/python3.10创建一个基于3.10的环境即使系统默认是3.9。创建速度更快。virtualenv用了缓存机制第二次创建相同版本的环境时会复用之前下载的Python二进制文件。跨平台一致性更好。Windows上的路径处理、权限管理比venv成熟。一个让我抓狂的场景某次部署到内网服务器Python版本是3.6.8但开发机是3.8.5。venv创建的环境直接报Fatal Python error: Py_Initialize: unable to load the file system codec。换成virtualenv指定--python/usr/bin/python3.6问题解决。注意virtualenv的--relocatable选项号称可以移动环境目录但实际效果不稳定。我试过三次两次报错。别依赖这个功能。conda数据科学家的瑞士军刀conda不是Python专属的虚拟环境工具它管理的是整个软件栈包括Python解释器、C库、R语言包等。Anaconda发行版自带condaMiniconda是精简版。conda create-ntf2python3.8tensorflow2.4conda activate tf2原理完全不同conda不依赖系统Python。它自己维护一个独立的包仓库channels包格式是.tar.bz2或.conda包含二进制文件、依赖元数据。创建环境时conda会下载指定版本的Python解释器比如Python 3.8.10的二进制包然后安装依赖。这意味着你可以在没有root权限的服务器上装任何版本的Python。优势解决非Python依赖。比如numpy依赖BLAS库opencv依赖libjpeg。pip只能装Python包conda可以装C库。我处理过一个项目需要libxml2的特定版本pip装不了conda一行搞定。环境克隆和导出。conda env export environment.yml可以精确记录所有包版本包括渠道来源。conda env create -f environment.yml在另一台机器上重建完全一致的环境。多版本Python共存。一个conda环境用Python 3.7另一个用3.10互不干扰。代价体积巨大。一个conda环境动辄几百MB因为包含了完整的Python解释器和依赖库。venv环境通常几十MB。包安装慢。conda的依赖解析算法SAT solver很慢尤其是包多的时候。conda install等几分钟是常事。与pip混用有风险。conda环境里用pip装包conda不知道这些包的存在可能导致依赖冲突。我见过有人先conda install tensorflow然后pip install torch结果两个框架的CUDA版本打架。最佳实践conda环境里尽量只用conda装包。如果必须用pip先装完所有conda包最后用pip。并且不要频繁在conda和pip之间切换。三者的选择策略选venv的场景项目简单只有纯Python依赖团队统一使用Python 3.8部署环境是Docker容器容器本身已经隔离了你不想多学一个工具选virtualenv的场景需要支持Python 2.7老项目遗留需要指定非默认Python版本创建环境频繁追求速度在Windows上开发需要更好的兼容性选conda的场景数据科学、机器学习项目numpy、pandas、tensorflow、pytorch需要管理非Python依赖C库、R包没有root权限需要安装特定Python版本需要精确复现环境学术研究、生产部署我的个人经验日常开发用venv就够了别为了炫技上conda数据科学项目必用conda省去编译C扩展的痛苦生产环境用Docker venvDocker负责系统级隔离venv负责Python包隔离永远不要用系统Python/usr/bin/python3直接跑项目除非你想重装系统一个实战案例从混乱到有序假设你接手一个项目requirements.txt里有Django2.2 numpy1.16.4 pandas0.24.2 scikit-learn0.21.3你的系统Python是3.9但Django 2.2只支持到Python 3.7。怎么办错误做法pip install -r requirements.txt然后祈祷。结果Django报错numpy编译失败pandas说版本不兼容。正确做法用conda创建环境conda create -n legacy_project python3.7激活环境conda activate legacy_project安装依赖conda install django2.2 numpy1.16.4 pandas0.24.2 scikit-learn0.21.3如果某个包conda仓库没有再用pippip install some-missing-package这样Python版本、C库依赖、Python包版本全部锁定。项目跑起来后conda env export environment.yml下次直接conda env create -f environment.yml重建。最后说几句虚拟环境不是银弹。它解决的是依赖隔离问题但解决不了代码质量问题。我见过有人一个项目里建了5个虚拟环境每个环境装不同的包代码里到处是sys.path.append——这是把问题复杂化了。一个项目一个环境环境文件requirements.txt或environment.yml纳入版本控制。别把虚拟环境目录提交到git里那是二进制文件diff毫无意义。还有别在生产服务器上激活虚拟环境跑代码。用Docker打包整个环境或者用pip的--target参数指定安装路径。激活脚本是给开发用的不是给生产用的。最后一条血泪教训定期清理不用的虚拟环境。我电脑上曾经有47个虚拟环境占了几十GB空间。conda env list一看一半是两年前的项目早就不维护了。conda env remove -n old_project干净利落。虚拟环境是工具不是信仰。选最适合你当前场景的那个别纠结。