简单而言,Python虚拟环境是一个文件夹结构,它提供轻量且隔离的Python环境所需的一切。
(本文均以Linux系统为例)
一个文件夹结构
当使用venv
模块创建虚拟环境时,Python创建一个自包含的文件夹,并复制或连接Python可执行文件到该文件夹结构下。
一个虚拟环境文件夹包含非常多的文件和文件夹,但是大部分都位于site-packages/
文件夹下,如果将对应文件夹下的子文件夹和文件都忽略,则整体结构不算太冗长:
venv/
│
├── bin/
│ ├── Activate.ps1
│ ├── activate
│ ├── activate.csh
│ ├── activate.fish
│ ├── pip
│ ├── pip3
│ ├── pip3.12
│ ├── python
│ ├── python3
│ └── python3.12
│
├── include/
│ │
│ └── python3.12/
│
├── lib/
│ │
│ └── python3.12/
│ │
│ └── site-packages/
│ │
│ ├── pip/
│ │
│ └── pip-24.2.dist-info/
│
├── lib64/
│ │
│ └── python3.12/
│ │
│ └── site-packages/
│ │
│ ├── pip/
│ │
│ └── pip-24.2.dist-info/
│
└── pyvenv.cfg
bin/
包含虚拟环境的可执行文件。最值得注意的是 Python 解释器 (python) 和 pip 可执行文件 (pip),以及它们各自的符号链接(python3、python3.12、pip3、pip3.12)。该文件夹还包含虚拟环境的激活脚本。具体的激活脚本取决于使用的 shell。include/
是一个初始为空的文件夹,Python 用它来包含 C 头文件,以便安装依赖于 C 扩展的软件包。lib/
包含嵌套在指定 Python 版本 (python3.12/) 文件夹中的 site-packages/ 目录。虚拟环境中使用的外部软件包将安装在该文件夹中。从 Python 3.12 开始,虚拟环境只预装了一个依赖包 pip。lib64
:出于兼容性考虑,许多 Linux 系统中的lib64/
都是以lib/
的符号链接形式出现的。某些 Linux 系统可能会根据其体系结构使用lib/
和lib64/
之间的区别来安装不同版本的库。{name}-{version}.dist-info/
是pip
的默认目录,包含软件包分发信息,用于记录已安装软件包的相关信息。pyvenv.cfg
是虚拟环境的关键文件。它只包含几个键值对,Python 使用它们来设置sys
模块中的变量,这些变量决定了当前 Python 会话将使用哪个 Python 解释器和站点软件包目录。
总之,宏观而言,一个虚拟环境有三个主要部分:
- Python二进制文件的副本或符号链接
pyvenv.cfg
文件site-packages
目录
site-packages/
内的安装包是可选的,但基本是默认存在的。即使该目录为空,虚拟环境仍然有效。
在默认设置下,venv
将只安装 pip
,这是安装 Python 软件包的推荐工具,毕竟安装其他软件包是虚拟环境最常见的使用情况。
若Python版本<3.12,则
site-packages/
目录下会有一些额外的文件夹:
setuptools
模块: Python 打包生态系统中的基本工具。setuptools
扩展了distutils
模块,提供了软件包发现、分发和依赖管理等功能。将setuptools
作为默认设置可确保用户无需安装其他工具就能立即使用这些功能。在采用 PEP 517 和 PEP 518 之前,许多软件包都依赖setuptools
进行安装。
_distutils_hack/
模块确保 Python 在安装软件包时,选择本地的._distutils
子模块而不是标准库的distutils
模块。
pkg_resources/
模块帮助应用程序自动发现插件,并允许 Python 软件包访问它们的资源文件。它与setuptools
一起发布。最后,还有一个名为
distutils-precedence.pth
的文件。该文件帮助设置distutils
导入的路径优先级,并与_distutils_hack
一起确保 Python 优先使用与setuptools
捆绑的distutils
版本,而不是内置版本。若 Python 版本>=3.12,那么这些文件与文件夹不会预装在虚拟环境中。即使使用的是旧版本的 Python,也不需要记住它们就能有效地使用虚拟环境。
只需记住,在
site-packages/
目录中预装的软件包都是标准工具,它们能让安装其他软件包变得更方便。
综上,Python虚拟环境只是一个文件架构,可以随时删除和重建。
隔离的Python安装
为构建一个隔离的环境以免安装的外部包与全局包冲突,venv
重建了标准Python安装会创建的文件夹结构。
如
一个文件夹结构
一节中指出的,该结构包括Python 二进制文件的副本或 symlink 的位置和 site-packages 目录(Python 安装外部软件包的目录)。虚拟环境中的 Python 可执行文件是环境所基于的 Python 可执行文件的副本还是符号链接,主要取决于操作系统。
Windows 和 Linux 可能会创建符号链接而不是副本,而 macOS 则总是创建副本。可尝试在创建虚拟环境时使用可选参数来影响默认行为。在大多数情况下,这个问题没啥影响。
值得注意的是,Python标准库模块并未包含于虚拟环境文件夹结构下,但依然可以在虚拟环境调用。这是因为虚拟环境重用了用于创建虚拟环境的 Python 安装中的 Python 内置模块和标准库模块。
因为创建虚拟环境总是需要一个现有的 Python 安装,venv 选择重用现有的标准库模块,以避免将它们复制到新虚拟环境的开销。正如 PEP 405 的动机所述,这种有意为之的行为加快了虚拟环境的创建速度,并使其更加轻量级。
除了标准库模块外,你还可以在创建环境时通过参数让虚拟环境访问基本安装的软件包:
$ python3 -m venv venv/ --system-site-packages
如果在调用 venv 时添加 --system-site-packages,Python 将把 pyvenv.cfg 中 include-system-site-packages 的值设置为 true。这一设置意味着你可以使用安装到基本 Python 中的任何外部软件包,就像将它们安装到虚拟环境中一样。
这种连接是单向的。即使你允许虚拟环境访问源 Python 的 site-packages 文件夹,你安装到虚拟环境中的任何新包也不会与那里的包混合。Python 会尊重安装到虚拟环境中的软件包的隔离性,并将它们放到虚拟环境中独立的 site-packages 目录中。
总而言之,虚拟环境基本上就是一个带有设置文件的文件夹结构。它可能预装了 pip,也可能没有,它可以访问源 Python 标准库模块,同时保持隔离。