Python 虚拟环境(Virtual Environment)是一个极其重要的概念,可以把它想象成每个 Python 项目都有一个独立的“沙盒”或“房间”。所有虚拟环境共用系统全局的 Python 解释器,但每个虚拟环境都有自己独立的依赖包库。

1. 为什么需要虚拟环境

想象一下,你的电脑是一个大房子,Python 是房子里的工具箱。

❌ 没有虚拟环境的情况(全局安装):

  • 你所有的工具(库/包)都扔在客厅的大箱子里。
  • 项目 A 需要 numpy 1.20 版本。
  • 项目 B 需要 numpy 1.25 版本。
  • 当你为项目 B 更新 numpy 时,项目 A 就崩溃了,因为它依赖旧版本。
  • 更糟糕的是,如果你不小心删错了文件,所有项目都可能受影响。
  • 这就是所谓的 “依赖冲突” 和 “环境污染”。

✅ 有虚拟环境的情况(隔离安装):

  • 每个项目都有自己独立的小房间(虚拟环境)。
  • 项目 A的房间里放着 numpy 1.20
  • 项目 B的房间里放着 numpy 1.25
  • 两个房间互不干扰。你在项目 B 里怎么折腾,都不会影响项目 A。
  • 当你想分享项目给同事时,只需要告诉他:“进这个房间,里面的工具都是配好的。”

2. 虚拟环境到底做了什么

当你运行 python3 -m venv .venv 创建虚拟环境时,Python 做了以下几件事:

  1. 复制解释器:它在 .venv 文件夹里创建了一个指向当前 Python 解释器的副本(或链接)。
  2. 独立库目录:它创建了一个空的 site-packages 文件夹,专门用来存放这个项目安装的库。
  3. 激活脚本:它生成了一些脚本(如 activate),用于切换当前终端的环境状态。

关键点:虚拟环境不是重新安装了一个完整的 Python,它只是创建了一个隔离的空间,让 pip install 安装的包只存在于这个空间里。

3. 如何使用虚拟环境

假设你正在开发一个名为 my_project 的项目。

第一步:创建虚拟环境

在项目根目录下运行:

python3.12 -m venv .venv

此时目录下多了一个 .venv 文件夹。

第二步:激活虚拟环境

你必须“进入”这个环境,才能使用它。以macOS / Linux为例:

source .venv/bin/activate

激活成功标志:你的命令行提示符前面会出现了 (.venv) 字样

(.venv) user@computer:~/my_project$
第三步:安装依赖

现在你安装的包只会在这个虚拟环境里:

pip install requests pandas
第四步:开发代码

正常运行你的 Python 脚本即可,它会自动使用环境里的 Python 和库。

第五步:退出虚拟环境

做完工作后,退出环境:

deactivate

提示符前的 (.venv) 消失,回到系统全局环境。

4. 如何保存和复现环境

为了让别人也能在你的项目上运行,你需要记录“房间里有哪些工具”。

4.1 导出依赖列表

在激活虚拟环境后,运行:

pip freeze > requirements.txt

这会生成一个 requirements.txt 文件,内容类似:

requests==2.31.0
pandas==2.1.1
numpy==1.25.2
4.2 复现环境

别人拿到你的项目后:

  1. 创建新的虚拟环境:python3 -m venv .venv
  2. 激活环境:source .venv/bin/activate
  3. 一键安装所有依赖:
pip install -r requirements.txt

这样,别人就能拥有和你完全一致的开发环境!

5. 虚拟环境与python解释器的关系

虚拟环境并没有复制一个完整的 Python 解释器,它只是创建了一组指向系统全局 Python 解释器的“快捷方式”或“链接”,并配合一个独立的包存储目录。可以把它们关系理解为:“同一个大脑(解释器),不同的记忆库(包)”。

5.1 核心关系图解

假设系统中安装了 Python 3.12位于 /usr/bin/python3.12。当运行 [ python3.12 -m venv .venv] 后,.venv 文件夹结构如下(以 Linux/macOS 为例):

.venv/
├── bin/
│   ├── python  ---> 符号链接 (Symlink) 指向 /usr/bin/python3.12
│   ├── python3 ---> 符号链接 (Symlink) 指向 /usr/bin/python3.12
│   └── pip     ---> 一个脚本,配置为只操作 .venv/lib/... 下的包
├── lib/
│   └── python3.12/
│       └── site-packages/  <--- 【关键】这里存放该项目独有的第三方库
└── pyvenv.cfg              <--- 配置文件,告诉 Python "我是虚拟环境"

文件夹内容详解:

  1. 解释器本体(Binary):

    • 虚拟环境中的 bin/python 不是一个新的可执行文件。
    • 它是一个符号链接(Symbolic Link),直接指向你创建时指定的那个全局 Python 解释器(如 /usr/bin/python3.12)。
    • 这意味着:无论你在多少个虚拟环境中,它们共享同一个底层的 Python 核心代码、标准库(如 ossysjson)和性能特性。
  2. 包管理路径(Site-packages):

    • 虽然解释器是同一个,但虚拟环境通过修改环境变量(主要是 PATH 和 PYTHONHOME),欺骗了解释器。
    • 当你在虚拟环境中运行 import requests 时,Python 解释器会优先去 .venv/lib/python3.12/site-packages/ 查找,而不是去全局的 /usr/lib/python3.12/site-packages/ 查找。
    • 这就是隔离的本质:解释器没变,但它“看”到的库变了。
  3. 配置文件(pyvenv.cfg):

    • 这个文件告诉 Python 解释器:“嘿,你现在处于一个虚拟环境中,请忽略全局的 site-packages,只看我这里的。”

5.2 全局 Python vs 虚拟环境中的 Python

特性 全局 Python (/usr/bin/python3.12) 虚拟环境中的 Python (.venv/bin/python)
物理文件 真实的二进制可执行文件 符号链接,指向全局 Python
版本 固定(如 3.12.1) 完全相同(因为指向同一个文件)
标准库 使用全局的标准库 使用全局的标准库(因为解释器一样)
第三方库路径 /usr/lib/.../site-packages .venv/lib/.../site-packages
独立性 所有项目共享,易冲突 独立,每个环境有自己的库
创建成本 需安装整个 Python 极速(只需创建链接和文件夹)

5.3 如何验证这种关系

可以在终端中亲自验证这种关系。

第一步:查看全局 Python 路径

$ which python3.12
/usr/bin/python3.12

第二步:创建并激活虚拟环境

$ python3.12 -m venv .venv
$ source .venv/bin/activate

第三步:查看虚拟环境中的 Python 路径

(.venv) $ which python
/home/user/project/.venv/bin/python

第四步:查看它是否是指向全局的链接

  • "箭头 ->" 就证明它是一个符号链接,指向全局解释器。
(.venv) $ ls -l $(which python)
lrwxrwxrwx 1 user user ... /home/user/project/.venv/bin/python -> /usr/bin/python3.12

第五步:查看包路径的不同

(.venv) $ python -c "import sys; print(sys.path)"
# 输出中会包含 '.venv/lib/python3.12/site-packages'
# 而不会包含全局的 site-packages(除非特别配置)

5.4 为什么这样设计

1. 节省空间

  • 如果每个虚拟环境都复制一份完整的 Python(包括标准库、二进制文件),每个环境可能要占用 100MB+。
  • 使用链接后,每个虚拟环境只占用几 MB(主要是你安装的第三方库)。

2. 保持一致性

  • 确保虚拟环境中的 Python 行为与系统安装的 Python 完全一致(同样的 bug,同样的性能,同样的 C 扩展兼容性)。

3. 易于升级

  • 如果你升级了系统全局的 Python 3.12(比如从 3.12.1 升到 3.12.2),所有指向它的虚拟环境会自动获得更新后的解释器内核(只要二进制接口兼容)。

5.5 总结

  • 虚拟环境 ≠ 新的 Python 安装。
  • 虚拟环境 = 全局 Python 解释器的快捷方式 + 独立的第三方库文件夹。
  • 关系:虚拟环境依赖于全局解释器存在。如果删除了全局的 Python 3.12,所有基于它创建的虚拟环境都会失效(因为链接断了)。

6. 常见疑问

Q1: 虚拟环境会占用很多磁盘空间吗?

  • 不会太多。它主要占用的是你安装的库的空间。Python 解释器本身是通过链接引用的,不会重复复制几个 GB 的文件。
  • 如果项目很小,虚拟环境可能只有几十 MB。

Q2: 我需要为每个项目都创建虚拟环境吗?

  • 强烈建议是的。这是 Python 开发的最佳实践。
  • 即使是很小的脚本,养成这个习惯能避免未来的麻烦。

Q3: .venv 文件夹要上传到 Git 吗?

  • 绝对不要!
  • .venv 包含特定于你操作系统的二进制文件,其他人无法直接使用。
  • 你应该在 .gitignore 文件中添加 .venv/,只上传 requirements.txt

Q4: 虚拟环境和 Conda 有什么区别?

  • venv 是 Python 自带的,轻量级,只管理 Python 包。
  • Conda 是一个更强大的包和环境管理器,可以管理非 Python 依赖(如 C 库、R 语言等),常用于数据科学领域。
  • 对于大多数 Web 开发或通用 Python 项目,venv 足够且更简单。

Q5: 虚拟环境.venv文件夹能否移动?

  • 不能移动!
  • 虚拟环境被移动后,内部的路径引用全部失效了。
  • 虚拟环境在创建时,会把绝对路径硬编码 到多个配置文件中,比如:

    • .venv/bin/activate → 里面硬编码了 VIRTUAL_ENV="/创建时路径/.venv"

    • .venv/pyvenv.cfg → 记录了 home = /opt/homebrew/bin 等路径信息

  • 当移动.venv文件夹后,这些内部记录的路径和实际位置不匹配了,虚拟环境失效

Q6: 我想给虚拟环境换个目录该怎么办?

  • 虚拟环境不能移动,只能在新位置重新创建。 
  • 操作如下:
    • 删除旧路径的虚拟环境:rm -rf  /xxx-old-path/.venv
    • 在新位置重新创建:python3.12 -m venv .venv
    • 激活并安装依赖:  source .venv/bin/activate  ,   pip install xxx

Q7: 虚拟环境里怎么该项目的python解释器?

  • PyCharm --> Settings  --> Project:my_xx_project --> Python Interpreter --> Add Interpreter --> Add Local Interpreter -->进入python解释器配置页面,解释器路径设置为“my_xx_project/.venv/bin/python”

更多推荐