Python venv模块(四)——虚拟环境如何工作

本文均以Linux为例。

复制结构与文件

当使用venv创建虚拟环境时,模块会重建操作系统上标准Python安装的文件和文件夹结构,从而保证Python可以如预期一样隔离地工作,而无需额外的改变。Python还会将调用venv模块时使用到的Python可执行文件的副本或符号链接也复制到该文件夹结构中:

venv/
│
├── bin/
│   ├── Activate.ps1
│   ├── activate
│   ├── activate.csh
│   ├── activate.fish
│   ├── pip
│   ├── pip3
│   ├── pip3.12
│   ├── python
│   ├── python3
│   └── python3.12
│
├── include/
│
├── lib/
│   │
│   └── python3.12/
│       │
│       └── site-packages/
│
├── lib64/
│   │
│   └── python3.12/
│       │
│       └── site-packages/
│
└── pyvenv.cfg

如果你找到系统安装Python的位置,并观察其目录结构,你会发现虚拟环境与其十分相似。

虚拟环境基于的基础Python安装在pyvenv.cfg 文件中的home键下可见。

调整前缀查找(Prefix-Finding)过程

有了标准的文件夹结构,只需根据 venv 规范对其前缀查找过程稍作调整,虚拟环境中的 Python 解释器就能了解所有相关文件的位置。

Python 解释器不是通过查找 os 模块来确定标准库的位置,而是首先查找 pyvenv.cfg 文件。如果解释器找到了这个文件,并且其中包含一个 home 关键字,那么解释器就会使用这个关键字来设置两个变量的值:

  • sys.base_prefix 将保存用于创建此虚拟环境的 Python 可执行文件的路径,您可以在 pyvenv.cfg 中的 home 关键字下定义的路径找到它。
  • sys.prefix 将指向包含 pyvenv.cfg 的目录。

如果解释器找不到 pyvenv.cfg 文件,那么它就会认为自己不是在虚拟环境中运行,这时 sys.base_prefixsys.prefix 都会指向相同的路径。

可通过查看sys.base_prefixsys.prefix变量内容来验证该过程。

>>> import sys
>>> sys.base_prefix
>>> sys.prefix

激活环境后:

>>> import sys
>>> sys.base_prefix
'/usr/local'
>>> sys.prefix
'/home/name/path/to/venv'

停用环境后:

>>> import sys
>>> sys.base_prefix
'/usr/local'
>>> sys.prefix
'/usr/local'

若上述两个变量值不同,则Python调整寻找模块的路径:

sitesysconfig标准库模块被修改以使标准库和头文件将相对于 sys.base_prefix […] 查找,而 site-package 目录 […] 仍相对于 sys.prefix […] 查找。

这一更改允许虚拟环境中的 Python 解释器使用基本 Python 安装中的标准库模块,同时指向其内部 site-packages 目录来安装和访问外部软件包。

链接回标准库

Python虚拟环境旨在提供一个轻量的提供隔离的Python环境的方法,从而可以快速地创建或删除。由此,venv只复制最小化的必要文件。

虚拟环境中的 Python 可执行文件可以访问作为环境基础的 Python 安装的标准库模块。Python 通过在 pyvenv.cfg 中的 home 设置中指向基本 Python 可执行文件的文件路径来实现这一点:

home = /usr/local/bin
include-system-site-packages = false
...

Python 通过将相关路径添加到 sys.path 来查找标准库模块。在初始化过程中,Python 会自动导入 site 模块,并为该参数设置默认值。

Python 会话在 sys.path 中可以访问的路径决定了 Python 可以从哪些位置导入模块。

如果激活虚拟环境并输入 Python 解释器,则可以确认基本 Python 安装的标准库文件夹路径可用:

>>> import sys
>>> sys.path
['',
 '/usr/local/lib/python312.zip',
 '/usr/local/lib/python3.12',
 '/usr/local/lib/python3.12/lib-dynload',
 '/home/name/path/to/venv/lib/python3.12/site-packages']

因为包含标准库模块的目录路径在 sys.path 中可用,所以在虚拟环境中使用 Python 时,可以导入任何标准库模块。

修改PYTHONPATH

为确保使用虚拟环境中的 Python 解释器来运行的脚本,venv 会修改 PYTHONPATH 环境变量(可以使用 sys.path 访问该变量)。

若未激活任何虚拟环境,访问该变量,则会看到默认Python安装的路径:

>>> import sys
>>> sys.path
['',
 '/usr/local/lib/python312.zip',
 '/usr/local/lib/python3.12',
 '/usr/local/lib/python3.12/lib-dynload',
 '/usr/local/lib/python3.12/site-packages']

其中'/usr/local/lib/python3.12/site-packages'site-packages目录,该目录包含了安装的外部包(如,使用pip安装的包)。在没有激活虚拟环境的情况下,该目录嵌套在与 Python 可执行文件相同的文件夹结构中。

Windows 上的Roaming文件夹包含一个额外的 site-packages 目录,该目录与使用 pip--user 标志的安装相关。该文件夹提供了一定程度的虚拟化,但仍将所有 --user 安装的软件包集中在一处。

若激活了虚拟环境,则sys.path变量值改变:

>>> import sys
>>> sys.path
['',
 '/usr/local/lib/python312.zip',
 '/usr/local/lib/python3.12',
 '/usr/local/lib/python3.12/lib-dynload',
 '/home/name/path/to/venv/lib/python3.12/site-packages']

即Python使用虚拟环境下的site-package目录路径更换了默认的路径,使得Python加载虚拟环境中的外部包。而且由于全局的site-packages目录未被列出,故Python不会加载其中的模块。

在 Windows 系统中,Python 还会将虚拟环境的根文件夹路径添加到 sys.path

由此,Python实现了虚拟环境中的外部包隔离。

此外,可以在创建虚拟环境时传递一个参数,从而实现以只读方式访问基本 Python 安装的系统 site-packages 目录。

激活时改变Shell的PATH变量

虽然不是必须的,但出于一致性考虑,一般推荐在在虚拟环境中工作之前先激活虚拟环境:

$ source venv/bin/activate
(venv) $

具体使用的激活脚本取决于操作系统以及使用的shell。

如果深挖虚拟环境的目录结构,将发现其附带了一些不同的激活脚本:

venv/
│
├── bin/
│   ├── Activate.ps1
│   ├── activate
│   ├── activate.csh
│   ├── activate.fish
│   ├── pip
│   ├── pip3
│   ├── pip3.12
│   ├── python
│   ├── python3
│   └── python3.12
│
├── include/
│
├── lib/
│
├── lib64/
│
└── pyvenv.cfg

这些激活脚本的作用是一样的。不同脚本用于适应不用的操作系统以及shell。

激活脚本有两个主要行为:

  • 路径:将VIRTUAL_ENV变量的值设定为虚拟环境的根目录,并将其 Python 可执行文件的相对位置预置到 PATH 中;
  • 命令提示符:由于脚本更改了命令提示符(在前面添加环境名),因此您可以很快知道虚拟环境是否已激活。

这两项更改都是并非必要的小改动,纯粹是为了方便使用。

当停用虚拟环境后,shell将会把改变恢复回去。

可在任意位置通过绝对路径使用

如前面的章节所述,无需激活虚拟环境即可使用之。

当在shell中只提供了二进制文件的名字时,shell将在PATH记录的路径中搜寻拥有该名字的可执行文件,选出并运行第一个批判的文件。

激活脚本更改了PATH变量以使shell首先在虚拟环境的二进制文件目录下搜寻可执行文件,从而使得用户可以仅用pippython这个名字便运行虚拟环境中的相应文件。

如果不激活环境的话,则可以通过使用对应Python的绝对路径来运行虚拟环境中的任何脚本:

$ /home/name/path/to/venv/bin/python

这个与激活环境再用python 调用是等价的。

参考资源

Python Virtual Environments: A Primer – Real Python

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值