Python 运行时

Python 运行时指的是 使 Python 程序能够执行的一组核心文件。从结构上看,主要包含三部分:

  1. 解释器(interpreter)
  2. Python 标准库(standard library)
  3. 内建模块(built-in modules)
  4. 扩展模块(extension modules)

第三方库(如 numpy、requests)通常安装在 site-packages 中,不属于 Python 自带运行时,但在执行视角下可以视为运行时的一部分。

抽象结构:

1
2
3
4
5
6
Python Runtime

├─ interpreter
├─ standard library
├─ built-in modules
└─ extension modules

Python 运行时的目录结构

在 Linux / macOS 的典型结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
python-3.12/
├── bin/
│ └── python3.12

├── lib/
│ └── python3.12/
│ ├── os.py
│ ├── pathlib.py
│ ├── importlib/
│ ├── encodings/
│ │
│ ├── lib-dynload/
│ │ ├── _ssl.cpython-312-x86_64-linux-gnu.so
│ │ └── _sqlite3.cpython-312-x86_64-linux-gnu.so
│ │
│ └── site-packages/

└── include/
└── python3.12/
└── Python.h

说明:

目录 作用
bin Python 解释器
lib/pythonX.Y Python 标准库
lib-dynload Python 自带的动态扩展模块
site-packages 第三方库安装目录
include C 扩展开发所需头文件

在 Windows 的结构与 Linux 略有不同,但逻辑一致:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Python312/
├── python.exe
├── python312.dll

├── Lib/
│ ├── os.py
│ ├── importlib/
│ ├── encodings/
│ │
│ ├── site-packages/
│ │
│ └── lib-dynload/

├── Scripts/
│ └── pip.exe

└── Include/

Python 启动时如何找到库

Python 启动后会构造 sys.path,用于查找模块:

1
2
3
4
5
6
7
[
script_dir_or_cwd,
PYTHONPATH,
stdlib,
lib-dynload,
site-packages,
]

含义:

路径 内容
script_dir / cwd 当前脚本目录或工作目录
PYTHONPATH 用户通过环境变量 PYTHONPATH 指定的额外路径
stdlib Python 标准库
lib-dynload 内置扩展模块
site-packages 第三方库

Python 会根据 解释器所在目录自动推导 最后的三种路径,使其保持固定的相对路径关系。

在导入模块时,例如

1
import requests

Python 会按顺序在这些路径中查找。

设置 PYTHONPATH 环境变量用于添加自定义搜索路径,常用于未安装脚本的临时调试,例如

1
PYTHONPATH=/my/path/to/sth python script.py

pip 安装库时发生了什么

pip 在安装库时,实际就是 将包文件安装到 site-packages,并写入安装元数据。

例如执行:

1
pip install requests

site-packages 会加上如下两个文件夹,分别存储实际的代码和安装元数据。

1
2
3
4
5
6
7
8
9
10
11
site-packages/
├── requests/
│ ├── __init__.py
│ ├── api.py
│ └── ...

├── requests-2.32.3.dist-info/
│ ├── METADATA
│ ├── RECORD
│ ├── entry_points.txt
│ └── ...

其中 .dist-info 目录专门记录包的安装信息,例如:

1
2
3
4
METADATA
RECORD
entry_points.txt
WHEEL

作用分别为:

文件 用途
METADATA 包信息
RECORD 安装文件列表
entry_points.txt CLI 工具入口
WHEEL 构建信息

如果一个包定义了 CLI 入口:

1
2
[project.scripts]
tool = "mypkg.cli:main"

安装时 pip 会生成对应的启动脚本/启动程序

1
2
3
4
5
# Linux / macOS
<python>/bin/tool

# Windows
Scripts/tool.exe

它们的作用只是按照安装时提供的说明,主动调用库中的对应函数,简化后的代码类似:

1
2
from mypkg.cli import main
main()

小结

Python 运行时结构:

1
2
3
4
5
6
7
8
Python Runtime

├─ interpreter
├─ built-in modules
├─ extension modules
└─ site-packages
├─ packages
└─ .dist-info

关键结论:

  • Python 运行时 = 解释器 + 标准库 + 内建模块和扩展模块
  • site-packages 存放第三方库
  • pip 只是在 site-packages写入包代码和元数据
  • Python 通过 sys.path 查找模块

Python 环境和虚拟环境

Python 环境

系统中可以同时安装多个 Python 运行时,通常是不同版本的 Python 或不同的 Python 实现,但是由于 PATH 的优先级顺序,默认只会使用找到的第一个 Python 运行时。

所谓 切换 Python 环境,本质上是调整查找顺序,让系统使用不同的 Python 运行时

实现的核心步骤就是修改 PATH,从而改变用户使用 pythonpython3 命令所调用的 Python 解释器。

在使用和安装库时,对应的 site-packages 位置是完全基于 Python 解释器位置进行相对路径推导的,因此切换了 Python 解释器也就附带切换了对应的环境。

环境 = 一个 Python 解释器 + 与其绑定的一组路径(sys.path)

Python 虚拟环境

虚拟环境是 Python 提供的一种隔离机制,用于创建彼此隔离的执行环境。其核心目标是:

为不同项目提供独立的第三方依赖(site-packages)环境。

虚拟环境不是独立的,必须依赖于一个已有的基础环境产生,也就是在一个基础的 Python 运行时中创建,下文中将其简称为 base。

在执行层面,可以将虚拟环境表示为:

1
2
3
4
5
6
7
Virtual Environment

├─ interpreter(指向或派生自基础 Python)
├─ stdlib(共享)
├─ built-in modules(共享)
├─ extension modules(共享)
└─ site-packages(独立)

需要强调的是:虚拟环境不等于环境,不会复制完整的 Python 运行时,虚拟环境仅对第三方库(site-packages)进行隔离,共享 base 的标准库和解释器。

由此带来的直接效果是:

  • 如果创建时使用的 Python 运行时被卸载或破坏,由其产生的虚拟环境也会失效。
  • 不能通过创建虚拟环境来切换 Python 版本,虚拟环境中的 Python 版本必然和 base 的 Python 版本是一样的。

虚拟环境的目录结构如下:(习惯上命名为 .venvvenv

Linux / macOS:

1
2
3
4
5
6
7
8
9
10
11
12
.venv/
├── bin/
│ ├── python
│ ├── pip
│ └── activate

├── lib/
│ └── python3.12/
│ └── site-packages/

├── include/
└── pyvenv.cfg

Windows:

1
2
3
4
5
6
7
8
9
10
11
.venv/
├── Scripts/
│ ├── python.exe
│ ├── pip.exe
│ └── activate.bat

├── Lib/
│ └── site-packages/

├── Include/
└── pyvenv.cfg

说明:

目录 作用
bin / Scripts 解释器与命令入口
site-packages 第三方库安装位置
pyvenv.cfg 虚拟环境配置核心
include C 扩展编译头文件

虚拟环境的行为由 pyvenv.cfg 控制,核心信息例如

1
2
3
home = /usr/bin
include-system-site-packages = false
version = 3.12.2

作用:

字段 含义
home base 的 Python 解释器位置
include-system-site-packages 是否继承 base 的 site-packages,通常都是 false
version Python 版本

实际的内容可能有所区别,例如 venv 创建的和 uv 创建的虚拟环境,记录的信息不完全一样。

用下面的命令可以激活虚拟环境(对于bash/fish/pwsh有各自的激活脚本)

1
source .venv/bin/activate

激活虚拟环境的实质是下面几件事:

  • 修改环境变量 PATH,使得用户优先找到虚拟环境中的 Python 解释器并使用,可以通过 which 命令查看
1
which python
  • 改变解释器前缀(prefix)

在虚拟环境中运行 Python,sys.prefixsys.base_prefix 会被改变,例如

1
2
sys.prefix       = .venv
sys.base_prefix = path to base Python
  • 改变 sys.path 模块查找路径,尤其是 site-packages 路径,这是虚拟环境隔离第三方依赖的核心机制

Python 启动后构造:

1
sys.path

在虚拟环境中典型为:

1
2
3
4
5
6
7
[
script_dir,
...
stdlib(来自 base Python)
lib-dynload(来自 base Python)
site-packages(来自虚拟环境)
]

因此在虚拟环境中使用的第三方模块是与 base 隔离的。

  • 与上一点相匹配的是安装机制,在使用虚拟环境中的 pip 下载的第三方库时,第三方依赖自然也会被安装到虚拟环境的 site-packages

  • 改变 shell 的 prompt,用来提示用户位于哪个虚拟环境

退出虚拟环境的实质就是撤销这部分改动,尤其是对 PATH 和 shell prompt 的改动。

小结

环境和虚拟环境的区别可以概况为:

  • 环境 = 一个 Python 解释器 + 与其绑定的一组路径(sys.path)
  • 虚拟环境 = 一个虚拟的 Python 解释器(指向 base Python) + 与其绑定的一组路径(sys.path),其中 site-packages 是虚拟环境独有的