Py学习  »  Python

不止会用 venv!深度拆解 Python 虚拟环境底层运行原理

AI思想会 • 1 周前 • 113 次点击  
【前言】AI 正以前所未有的速度发展,新的机遇不断涌现,如果你希望:与技术专家、产品经理和创业者深度交流,一起探索 AI如何改变各行各业。欢迎在文末扫二维码,加入「AI思想会」交流群,和一群志同道合的伙伴共同学习、思考、创造!

在很久很久以前,Python的包管理非常混乱,这导致不同的项目几乎无法同时运行,所以就出现了Python 环境管理的概念,不同项目的环境彼此隔离,开发者可以为不同项目分别管理依赖项、Python 版本和软件包。弄清其底层工作机制有助于排查依赖问题、构建可复现的应用程序,也便于管理复杂的 Python 项目。


图片

什么是 Python 环境?


从文件系统来看,Python 环境就是包含以下内容的一套目录结构:

  1. Python 解释器,或者指向解释器的符号链接

  2. 存放已安装库的 site-packages 目录

  3. 修改 shell 环境变量的激活脚本

  4. 管理软件包的 pip 和安装工具

创建虚拟环境,相当于建立一套隔离的 Python 安装。它不会干扰系统 Python,也不会影响其他项目的环境。


虚拟环境的技术组成


目录结构


执行 python -m venv myenv 后会生成以下目录结构:


 myenv/   ├── bin/ (or Scripts/ on Windows)   │   ├── python -> /usr/bin/python3  # Symlink to base interpreter   │   ├── python3 -> python   │   ├── pip   │   ├── activate                    # Bash activation script   │   ├── activate.csh                # C shell script   │   ├── activate.fish               # Fish shell script   │   └── Activate.ps1                # PowerShell script   ├── include/   │   └── python3.x/                  # C headers for building extensions   ├── lib/   │   └── python3.x/   │       └── site-packages/          # Where pip installs packages   └── pyvenv.cfg                      # Configuration file


pyvenv.cfg 文件


环境的行为由这份配置文件决定:


 home = /usr/bin   include-system-site-packages = false   version = 3.11.4   executable = /usr/bin/python3.11   command = /usr/bin/python3 -m venv /path/to/myenv


关键参数包括:

  • home:基础 Python 解释器的路径

  • include-system-site-packages:是否继承系统软件包(默认值:false)

  • version:Python 版本

  • executable:创建环境时使用的原始 Python 二进制文件


深入了解环境创建过程


第 1 步:创建目录结构


运行 python -m venv myenv 时,Python 的 venv 模块开始创建环境。它先建立基础目录及其子目录。随后复制 Python 二进制文件,或者建立指向它的符号链接。Unix 使用符号链接,Windows 则复制二进制文件。接着创建 site-packages 目录。最后写入 pyvenv.cfg 配置。


第 2 步:安装核心软件包


环境中会预装以下软件包:

  • pip:软件包安装器

  • setuptools:构建和分发工具

  • wheel:二进制软件包格式支持(可选)

安装所需文件来自下载,或者直接从基础 Python 安装中复制。


第 3 步:生成激活脚本


激活脚本因平台而异,执行时会完成四项操作:

  1. 保存当前环境状态($PATH、$PYTHONHOME)

  2. 修改环境变量

 VIRTUAL_ENV="/path/to/myenv"    PATH="$VIRTUAL_ENV/bin:$PATH"    unset PYTHONHOME  # Prevent conflicts
  1. 更改 shell 提示符,添加 (myenv) 前缀

  2. 定义 deactivate 函数,以便恢复原始状态


示例:激活期间会发生什么


 # Before activation   $ which python   /usr/bin/python      $ echo $PATH   /usr/local/bin:/usr/bin:/bin      # After: source myenv/bin/activate   $ which python   /path/to/myenv/bin/python      $ echo $PATH   /path/to/myenv/bin:/usr/local/bin:/usr/bin:/bin      $ echo $VIRTUAL_ENV   /path/to/myenv


理解 sys.path:模块搜索路径


sys.path 是一个目录路径列表。导入模块时,Python 会逐一搜索其中的目录;它也是 Python 导入系统最关键的组成部分。


sys.path 如何构建


Python 解释器启动后,按以下顺序构建 sys.path:

  1. 脚本所在目录;在交互模式下则是当前目录

  2. PYTHONPATH 环境变量中的目录(如果已设置)

  3. 取决于安装的默认路径,包括标准库位置、site-packages 目录,以及 site-packages 中的 .pth 文件


虚拟环境中的 sys.path


 import sys   print(sys.path)


激活环境后的输出:


 [       '',  # Current directory       '/path/to/myenv/lib/python3.11/site-packages',  # Virtual env packages       '/usr/lib/python3.11',                          # Standard library       '/usr/lib/python3.11/lib-dynload',              # Dynamic modules       # System site-packages EXCLUDED (if include-system-site-packages=false)   ]


导入解析如何工作


执行 import numpy 后,Python 按顺序遍历 sys.path。它会在每个目录中寻找 numpy.py(单文件模块)、numpy/__init__.py(软件包),或者 numpy.so、numpy.pyd(编译扩展)。首个匹配项会被导入;如果全部路径都没有匹配项,则引发 ModuleNotFoundError。


site 模块


Python 启动时会自动导入 site 模块。该模块负责把 site-packages 目录加入 sys.path,处理可以增加额外路径的 .pth 文件,并设置用户 site-packages(~/.local/lib/pythonX.Y/site-packages)。相关路径可以直接检查:


 import site   print(site.getsitepackages())  # ['path/to/myenv/lib/python3.11/site-packages']   print(site.getusersitepackages())  # User's site-packages directory


技术深潜:虚拟环境如何实现隔离


1、解释器发现


运行 myenv/bin/python 时,二进制文件先检查当前目录或父目录中是否存在 pyvenv.cfg。找到配置后,它读取 home 来定位基础解释器。标准库仍取自基础解释器,site-packages 则使用虚拟环境内的目录。


2、ys.prefix 机制


 import sys   print(sys.prefix)        # /path/to/myenv   print(sys.base_prefix)   # /usr (base installation)
  • sys.prefix:虚拟环境根目录

  • sys.base_prefix:原始 Python 安装目录

  • sys.prefix != sys.base_prefix:表示当前处于虚拟环境中


借助这套机制,Python 从基础安装加载标准库,却把软件包安装到虚拟环境的 site-packages。


3、为什么符号链接与复制很重要


Unix/Linux 使用符号链接,可以节省磁盘空间并自动继承基础 Python 的更新;如果基础 Python 被移除,链接也可能随之失效。


Windows 复制二进制文件,占用的磁盘空间更多,却不会因基础 Python 发生变更或被移除而受影响,所以Python 更新后必须重新创建虚拟环境。


环境管理工具比较


venv(内置,Python 3.3+)


优点:

  • 不需要外部依赖

  • 属于官方标准

  • 轻量

缺点:

  • 无法为不同 Python 版本创建环境

  • 功能比替代工具少

virtualenv(第三方)


优点:

  • 支持 Python 2.7+

  • 创建环境更快

  • 配置选项更多

  • 可以使用不同的 Python 版本

技术差异在于,virtualenv 使用发现机制寻找 Python 解释器。


conda(Anaconda/Miniconda)


优点:

  • 一并管理 Python 版本和软件包

  • 能处理非 Python 依赖项,包括 C 库和 R 软件包

  • 采用二进制软件包分发

技术架构:

  • 完全独立于 pip

  • 使用自己的软件包索引和依赖求解器

  • 创建完整的 Python 安装,而不只是一套虚拟环境


进阶:动态修改 sys.path


1、 直接操作


 import sys   sys.path.insert(0, '/custom/path/to/modules')  


    
 import my_custom_module


2、使用 .pth 文件


创建 myenv/lib/python3.11/site-packages/custom.pth:


 /path/to/my/modules   /another/path


启动时,这些路径会自动加入 sys.path。


3、使用 PYTHONPATH


 export PYTHONPATH=/path/to/modules:$PYTHONPATH   python script.py


警告:PYTHONPATH 会影响所有 Python 进程,可能引发冲突。


环境管理一些技巧


  1. 每个项目使用一个环境,避免依赖冲突

  2. 使用 requirements.txt 固定版本,保证环境可复现

  • pip freeze > requirements.txt pip install -r requirements.txt

  1. 切勿提交虚拟环境,应将其加入 .gitignore

  2. 记录 Python 版本,可使用 .python-version 或在 README 中说明

  3. 使用环境变量,在代码之外存储密钥和配置


调试环境问题


检查正在运行哪个 Python


 import sys   print(sys.executable)      # /path/to/myenv/bin/python   print(sys.prefix)          # /path/to/myenv   print(sys.path)            # Module search paths


验证软件包位置


 import numpy   print(numpy.__file__)  # Should be in your virtual env's site-packages


检查名称遮蔽


 # If wrong module is imported, check sys.path order   python -c "import sys; print('\n'.join(sys.path))"


总结


Python 环境不只是一组隔离目录。文件系统组织(site-packages、bin 目录)、配置文件(pyvenv.cfg)、环境变量(PATH、VIRTUAL_ENV、PYTHONPATH)、运行时内省(sys.path、sys.prefix)和导入钩子(site 模块)共同构成了整套系统。


理解这些机制,才能妥善排查复杂的依赖问题、构建自定义部署 Pipeline,并设计稳健的 Python 应用程序。sys.path 的模块搜索机制配合虚拟环境隔离,既保留 Python 的灵活性,也维持各项目之间的独立性。


作者:Darshika pundir

本文系学术转载,如有侵权,请联系后台删文
(👇 长按识别二维码)
图片
转自:DeepHub IMBA

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/200590