Python虚拟环境实战:venv、conda与requirements.txt全解析
1. 为什么你写的 Python 代码在同事电脑上跑不起来?——虚拟环境不是“可选项”,是“生存线”
我第一次把写好的爬虫发给测试同事时,他回了我一句:“ImportError: No module named requests”。我愣了三秒,立刻截图发过去:“你 pip install requests 一下就行。”五分钟后,他回复:“pip 不是内部或外部命令。”我当场打开远程桌面,发现他电脑上压根没装 pip,Python 是从官网下载的 .exe 安装包,勾选了“Add Python to PATH”但没勾“Install pip”——这选项默认不勾。更讽刺的是,他系统里还躺着一个 Anaconda 自带的 Python,PATH 里两个 Python 路径打架,pip 指向了 conda 环境,而python命令却调用了 C:\Python39\python.exe。
这就是没有虚拟环境的真实代价:你不是在写 Python 代码,你是在和别人的操作系统、Python 版本、全局包管理器、PATH 环境变量打一场没有规则的混战。
“零基础入门虚拟环境”这句话背后藏着一个残酷事实:绝大多数 Python 新手根本不知道自己正在用“裸奔模式”开发。他们以为pip install就是安装,import xxx就是导入,直到某天pip install tensorflow把整个项目的numpy==1.19.5覆盖成numpy==2.0.0,导致pandas.read_csv()直接报错;或者pip install flask顺手升级了click,结果本地flask run正常,部署到服务器却提示click.echo() got an unexpected keyword argument 'err'——因为服务器上旧版click不支持err参数。
关键词里的venv、requirements.txt、pip,不是三个孤立工具,而是一套最小闭环生存系统:
venv是你的“隔离舱”,它不复制 Python 解释器,而是用符号链接(Windows 下是硬链接)快速创建一个干净、独立、与系统 Python 完全解耦的运行空间;pip是这个舱内的“物资补给官”,它只管舱内仓库,绝不触碰舱外世界;requirements.txt是你的“舱内物资清单”,它精确记录每一件物资的名称和版本号,确保下次重建隔离舱时,一模一样。
你不需要懂sys.path如何被修改,也不必研究site-packages的加载顺序。你只需要记住:只要没激活虚拟环境,你就没资格说“我的 Python 环境配置好了”。这不是仪式感,是工程底线。哪怕你只是写一个 10 行的脚本处理 Excel 表格,也该为它建个venv。因为下一次你可能要加一行import openpyxl,而openpyxl依赖的et-xmlfile又会悄悄覆盖掉你另一个项目里正在用的lxml==4.9.3。混乱从来不是从大型项目开始的,它始于第一个pip install没加-i镜像源、没加--user、也没进虚拟环境的那一刻。
2. venv 与 conda:别再被“哪个更好”困住——先搞懂它们解决的根本问题不同
搜索热词里反复出现 “anaconda 创建虚拟环境”、“conda 创建 python 虚拟环境”、“miniforge 虚拟环境”,甚至还有人问 “conda 和 venv 能不能一起用”。这说明一个普遍误区:把 venv 和 conda 当成同一类工具的两种品牌,试图做“二选一”。实际上,它们连“赛道”都不一样。
我们来拆解最核心的差异点:venv 管理的是 Python 包(packages),conda 管理的是软件包(packages + binaries + environments)。
python -m venv myenv创建的环境,本质是:- 复制一份
python.exe(或python可执行文件)的引用; - 创建一个空的
site-packages文件夹; - 修改
activate.bat/activate.sh,让PATH临时指向这个新bin/Scripts目录; - 它完全不碰 Python 解释器本身——你用什么版本的 Python 安装的 venv,环境里就是什么版本,无法跨版本切换。
- 复制一份
conda create -n myenv python=3.11创建的环境,本质是:- 从 conda 渠道(如 defaults, conda-forge)下载并解压一个完整、预编译的 Python 3.11 解释器二进制包;
- 同时下载所有依赖库(如
openssl,zlib,sqlite)的二进制文件,一并放进环境目录; - 它管理的是整个“运行时栈”,包括 C 库、Fortran 编译器、甚至 R 语言解释器。
所以,当你看到 “conda 删除虚拟环境” 或 “conda 虚拟环境部署 facefusion”,你就该明白:facefusion 这类项目重度依赖 OpenCV、ONNX Runtime、CUDA 驱动等底层二进制组件,它们有复杂的 ABI 兼容性要求。pip install opencv-python下载的是 wheel 包,里面打包了预编译的.so或.dll,但它只保证与当前 Python 版本兼容,不保证与你的显卡驱动、CUDA 版本匹配。而conda install opencv下载的是 conda-forge 编译的包,它明确声明了所依赖的cudatoolkit=11.8、cudnn=8.6,conda 会自动帮你拉取匹配的版本,避免ImportError: libcudnn.so.8: cannot open shared object file这种经典报错。
那新手该选哪个?答案很务实:
- 如果你学的是Web 开发(Django/Flask)、数据分析(pandas/numpy)、自动化脚本、爬虫,无脑用
venv。理由极其简单:Python 官方内置,无需额外安装,学习成本为零,pip生态无缝对接,requirements.txt标准统一。你用pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ flask,它就老老实实装 flask,不会顺手给你换掉系统 Python。 - 如果你做的是AI/ML(PyTorch/TensorFlow)、科学计算(SciPy)、需要 CUDA 加速、或必须用特定 Fortran 编译器的数值模拟,直接上 conda/miniforge。因为它能一次性解决“Python 解释器 + C/C++ 运行时 + GPU 驱动库 + 科学计算 BLAS 库”的全栈兼容性问题。
conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia这一条命令,背后是 conda 在数十个二进制包中精准匹配 ABI 版本,这是 pip 做不到的。
提示:
venv和conda并非互斥。你可以用 conda 创建一个纯净的 Python 环境(conda create -n py311 python=3.11),然后在这个 conda 环境里再用python -m venv myproject_env创建子环境。这种“conda 管 Python 版本,venv 管项目依赖”的嵌套模式,在大型团队中很常见——conda 解决基础运行时一致性,venv 解决项目级依赖隔离。
3. 从零创建、激活、使用虚拟环境:Windows/macOS/Linux 三端实操细节全解析
很多教程写“打开终端,输入python -m venv myenv”,然后就跳到source myenv/bin/activate。但新手卡在第一步:“终端在哪?”、“python 命令不存在?”、“venv 文件夹创建后是空的?”这些不是小问题,是真实阻碍。下面我按操作系统,把每个“看似简单”的步骤掰开揉碎,告诉你为什么这么操作、哪里容易出错、以及出错后怎么救。
3.1 Windows 系统:CMD、PowerShell、Git Bash 的激活方式完全不同
Windows 上最大的坑,就是PowerShell 默认禁用脚本执行策略。当你在 PowerShell 里输入myenv\Scripts\activate.bat,它会报错:“无法加载文件 myenv\Scripts\activate.ps1,因为在此系统中禁止执行脚本。” 这不是你的环境坏了,是 PowerShell 的安全策略在起作用。
正确做法分三步:
确认 Python 已正确安装并加入 PATH:
- 按
Win+R,输入cmd回车; - 输入
python --version,应显示Python 3.x.x; - 输入
where python,应返回类似C:\Users\YourName\AppData\Local\Programs\Python\Python311\python.exe的路径; - 如果提示“不是内部或外部命令”,请重新安装 Python,务必勾选 “Add Python to PATH”(安装器最下方那个小勾选框,很多人忽略)。
- 按
创建虚拟环境(推荐用 CMD,避开 PowerShell 权限问题):
# 进入你的项目文件夹,比如 D:\myproject cd /d D:\myproject # 创建名为 "venv" 的虚拟环境(名字随意,但 "venv" 是行业惯例) python -m venv venv执行后,你会看到项目目录下多了一个
venv文件夹。它不是空的!里面包含:venv\Scripts\:存放python.exe,pip.exe,activate.bat(CMD 用),Activate.ps1(PowerShell 用);venv\Lib\site-packages\:初始为空,pip install后包会装在这里;venv\pyvenv.cfg:配置文件,记录 base Python 路径和是否 include system site-packages。
激活环境(关键!不同终端用不同命令):
- CMD 用户:
venv\Scripts\activate.bat - PowerShell 用户:先临时绕过策略(仅本次会话有效):
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser venv\Scripts\Activate.ps1 - Git Bash 用户:
source venv/Scripts/activate(注意是正斜杠/,且activate无.bat或.ps1后缀)
- CMD 用户:
注意:激活成功后,命令行提示符前会出现
(venv)字样,例如(venv) C:\myproject>。这是唯一可靠的激活成功标志。如果没看到(venv),说明没激活,后续所有pip install都会装到全局环境!
3.2 macOS/Linux 系统:权限、Shell 类型、路径分隔符的隐藏雷区
macOS 和 Linux 用户常遇到的问题是:source venv/bin/activate后,pip命令还是找不到,或者python版本没变。根源往往在 Shell 类型和 PATH 设置。
确认你的默认 Shell:
echo $SHELL # 输出 /bin/zsh(macOS Catalina+ 默认)或 /bin/bash如果你用的是 zsh,但教程教的是 bash 的
source命令,它依然有效,因为source是 POSIX 标准命令。但如果你用的是 fish shell,source就不工作,得用source venv/bin/activate.fish。创建与激活(标准流程):
# 进入项目目录 cd ~/Projects/myproject # 创建虚拟环境(注意:Linux/macOS 用 "venv" 文件夹名,路径分隔符是 "/") python3 -m venv venv # 激活(zsh/bash 都适用) source venv/bin/activate激活后,
which python应返回~/Projects/myproject/venv/bin/python,which pip返回同目录下的pip。常见故障排查:
/usr/bin/python: no module named venv:说明你用的是系统自带的 Python 2.7(macOS 10.14 及更早版本)。解决方案:用 Homebrew 安装 Python 3:brew install python3,然后用python3 -m venv venv;- 激活后
pip仍报错 “command not found”:检查venv/bin/目录下是否有pip文件(ls -l venv/bin/pip)。如果没有,说明创建失败,删除venv文件夹重试; pip install报错 “Permission denied”:绝对不要加sudo!这会破坏虚拟环境的隔离性。正确做法是确保你对venv文件夹有读写权限:chmod -R u+rw venv。
3.3 统一验证法:三步确认你的虚拟环境真正“活”了
无论哪个系统,激活后必须做这三件事,缺一不可:
检查 Python 解释器路径:
which python # macOS/Linux where python # Windows CMD # 输出必须是项目目录下的 venv/bin/python 或 venv\Scripts\python.exe检查 pip 是否指向虚拟环境内:
which pip pip --version # 输出应显示 pip 位置在 venv 内,且版本号后带 "(from venv)"检查 site-packages 是否为空(初始状态):
python -c "import site; print(site.getsitepackages())" # 输出应是一个路径列表,第一个路径就是 venv/lib/python3.x/site-packages/ # ls -l venv/lib/python3.x/site-packages/ # 应只有 __pycache__ 和 easy-install.pth 等基础文件
提示:如果以上三步任一失败,请立即停手。不要继续
pip install。先删除整个venv文件夹,重新python -m venv venv,再严格按上述步骤激活。虚拟环境的“洁净度”比速度重要一百倍。
4. requirements.txt:不是备份文件,是项目可复现性的法律契约
很多新手把requirements.txt当成一个“顺便生成的清单”,pip freeze > requirements.txt一下就完事。但当他们把这份文件发给同事,同事pip install -r requirements.txt后,项目依然报错,于是抱怨 “requirements.txt 没用”。真相是:他们生成的是一份“污染清单”,而不是“契约清单”。
pip freeze的本质是:列出当前环境中所有已安装包及其精确版本号(包括你无意中装的、依赖传递带进来的、甚至系统级的包)。它不区分“项目必需”和“偶然存在”。比如你为了调试装了个jupyter,pip freeze就会把它写进去;你用pip install flask时,flask依赖Werkzeug,Jinja2,itsdangerous,这些也会被列出来。但requirements.txt的使命,是定义“这个项目启动所需的最小依赖集合”,而不是“这个环境里有什么”。
4.1 正确生成 requirements.txt 的三种场景与对应方法
| 场景 | 问题 | 推荐方法 | 命令示例 | 原理 |
|---|---|---|---|---|
| 全新项目,手动安装依赖 | 你只装了flask,requests,sqlalchemy,但pip freeze会列出 20+ 个包 | pipreqs工具 | pip install pipreqspipreqs ./ --encoding=utf8 | pipreqs扫描项目 Python 文件中的import语句,只生成实际被代码引用的包,忽略传递依赖和调试工具 |
| 已有项目,需精确控制版本 | flask的最新版 2.3.0 有 breaking change,你只想锁定flask==2.2.5 | 手动编辑 +pip install -e . | 创建setup.py,在install_requires中写flask==2.2.5pip install -e . | -e模式(editable install)让 pip 把当前目录当作一个可安装包,setup.py中的依赖声明才是权威来源 |
| 生产环境部署,需绝对稳定 | 你希望numpy永远是1.24.3,不接受任何1.24.x的微更新 | pip freeze+ 人工精简 | pip freeze > requirements.in手动删掉 jupyter,ipython,wheel等非必需项pip install -r requirements.in | requirements.in是“原料清单”,requirements.txt是“成品清单”。部署时用pip install -r requirements.txt,确保字节级一致 |
4.2 requirements.txt 的黄金书写规范(避坑指南)
一份专业的requirements.txt必须遵守以下四条铁律:
每行一个包,格式为
package_name==version:- ✅
requests==2.31.0 - ❌
requests >= 2.30.0(版本浮动会导致不可复现) - ❌
requests(无版本号,下次pip install可能装 3.0.0)
- ✅
使用
#注释说明用途:# Web 框架核心 flask==2.2.5 # 数据库 ORM sqlalchemy==2.0.23 # 用于处理 Excel 文件 openpyxl==3.1.2将开发依赖(dev dependencies)分离:
- 创建
requirements-dev.txt,内容如:# 开发时用,生产环境不需 pytest==7.4.3 black==23.10.1 mypy==1.7.1 - 安装时:
pip install -r requirements.txt(生产依赖) +pip install -r requirements-dev.txt(开发依赖)
- 创建
指定镜像源,避免国内用户安装失败:
- 在
requirements.txt第一行添加:-i https://pypi.tuna.tsinghua.edu.cn/simple/ - 或者全局配置:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple/
- 在
实操心得:我曾维护一个 50 人的数据科学项目,某次
pip freeze > requirements.txt后,一位同事的matplotlib升级到了 3.8.0,导致所有plt.show()图形窗口无法关闭。后来我们强制规定:所有requirements.txt必须由pipreqs生成,并在 CI 流水线中加入校验步骤——pipreqs扫描结果必须与requirements.txt完全一致,否则构建失败。这成了我们项目的“法律契约”。
5. 真实排错链路:从 “pip 不是内部或外部命令” 到 “成功运行第一个 Flask 应用”
现在,我们把前面所有知识点串起来,走一遍最典型的新手排错全流程。这不是假设,而是我每天在技术群、Stack Overflow 上看到的真实案例。
5.1 故障现场还原:用户提问 “pip : 无法将‘pip’项识别为 cmdlet、函数、脚本文件或可运行程序的名称”
用户环境:Windows 11,Python 3.11 从官网下载安装,勾选了 “Add Python to PATH”,但没勾 “Install pip”。他打开 PowerShell,输入pip --version,报错如题。
我的排查思路(不是直接给答案,而是展示如何思考):
第一层:确认 pip 是否真的存在?
where pip(CMD)或Get-Command pip(PowerShell)→ 返回空,证明 pip 可执行文件确实没生成;dir "C:\Users\YourName\AppData\Local\Programs\Python\Python311\"→ 查看目录下是否有Scripts\pip.exe;- 结果:
Scripts文件夹里只有python.exe,pythonw.exe, 没有pip.exe。
第二层:为什么 pip 没安装?
- 回顾 Python 官网安装器界面:有一个独立的复选框 “Install pip”,它和 “Add Python to PATH” 是两个独立选项;
- 用户只勾了后者,忘了前者。这是 Python 安装器的 UX 设计缺陷,也是新手最高频的坑。
第三层:如何修复?
- 方案 A(推荐):重装 Python,这次两个都勾;
- 方案 B(应急):手动安装 pip:
- 下载
get-pip.py(https://bootstrap.pypa.io/get-pip.py); - 在 CMD 中运行:
python get-pip.py; where pip应返回C:\Users\YourName\AppData\Local\Programs\Python\Python311\Scripts\pip.exe。
- 下载
5.2 进阶故障:激活虚拟环境后,pip install flask成功,但python app.py报错 “ModuleNotFoundError: No module named 'flask'”
用户环境:已成功创建并激活venv,which pip指向venv/Scripts/pip,pip install flask显示 “Successfully installed flask-2.2.5”,但运行python app.py时仍报错。
排查链路:
确认
python命令是否真的调用虚拟环境内的解释器?where python→ 返回C:\Python311\python.exe(系统 Python),而非venv\Scripts\python.exe;- 原因:Windows 上,
activate.bat只修改了PATH,但python命令的优先级可能被系统 PATH 中更靠前的 Python 覆盖; - 验证:
venv\Scripts\python.exe app.py→ 成功!证明问题出在python命令的解析上。
根本解决方案:永远用
python而不是py或系统python:- 在激活状态下,
python命令会被venv\Scripts\目录下的python.exe拦截; - 但如果 PATH 中有多个 Python,保险起见,在项目根目录下,直接运行
venv\Scripts\python.exe app.py; - 或者,在 PyCharm/VSCode 中,将 Python 解释器路径手动设置为
venv\Scripts\python.exe。
- 在激活状态下,
5.3 终极验证:用 10 行代码跑通 Flask,完成闭环
现在,我们用一个最简 Flask 应用,验证整个虚拟环境链路是否真正打通:
在已激活的
venv中,创建app.py:from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return "Hello from Virtual Environment!" if __name__ == '__main__': app.run(debug=True)安装 Flask 并运行:
# 确保已激活 (venv) pip install flask==2.2.5 python app.py访问
http://127.0.0.1:5000,看到 “Hello from Virtual Environment!”- ✅ 成功!这意味着:
- Python 解释器来自
venv; flask包安装在venv\Lib\site-packages\;import flask从venv加载,而非全局;- 整个依赖链路干净、隔离、可复现。
- Python 解释器来自
- ✅ 成功!这意味着:
最后分享一个小技巧:在 VSCode 中,按
Ctrl+Shift+P(Windows)或Cmd+Shift+P(macOS),输入 “Python: Select Interpreter”,然后选择你项目下的venv\Scripts\python.exe。VSCode 会自动在底部状态栏显示(venv),并且集成终端(Terminal)会自动激活该环境。这是目前最丝滑的开发体验,比手动activate高效十倍。
我第一次用虚拟环境跑通 Flask 时,盯着浏览器里那行 “Hello from Virtual Environment!” 看了足足一分钟。不是因为代码多牛,而是因为我知道,从此以后,我的每一个pip install,都不再是向混沌宇宙投掷一颗石子,而是在亲手搭建一座可控、可预测、可交付的数字方舟。这,就是 Python 工程化的起点。
