bigywater.tech
Python · 86

Python 解释器启动过程:从进程到执行脚本

python script.py 按下回车到你的第一行代码执行之间,解释器做了什么?这篇不查资料——直接让 Python 自己汇报:用 -X importtime 看导入顺序,用 -S、-I 看开关的作用,再往 site-packages 里放一个 .pth 文件,亲眼看它在你的脚本之前执行。

5434 µs
site 模块在一次空启动里的累计耗时,最重的一项
54 → 21
默认启动与 -S 启动之后 sys.modules 里的模块数
.pth 先于脚本
放在 site-packages 里的 .pth 中的 import 行,在主脚本之前被执行

验证一:启动时导入了什么、按什么顺序

python -X importtime -c pass
实测 · 本机执行输出
  共 45 行导入记录, 其中最外层(不是被别的模块触发)的 7 个, 顺序如下 (自身耗时 µs / 累计 µs):
    1. _frozen_importlib_external 自身   319   累计   564
    2. zipimport                  自身   121   累计   421
    3. encodings                  自身   333   累计   928
    4. encodings.utf_8            自身   105   累计   105
    5. _signal                    自身    41   累计    41
    6. site                       自身   235   累计  5434
    7. linecache                  自身   149   累计   149
  最外层累计耗时合计 ≈ 7642 µs
  其中 site 模块(处理 site-packages / .pth / sitecustomize)一项累计 5434 µs

一次空启动记录了 45 行导入(含被别的模块触发的),其中最外层只有 7 个:先是 _frozen_importlib_external(让 import 自己能工作)和 zipimport,再是 encodings/utf_8(文本编码),_signal,然后是 site——它一项就占 5434 µs,是最大的开销,最后 linecache。这些都发生在你的脚本之前。

验证二:开关的效果

实测 · 本机执行输出
  python -c pass                           18.8 ms   启动后 sys.modules 有 54 个
  python -S -c pass  (不 import site)       12.8 ms   启动后 sys.modules 有 21 个
  python -I -c pass  (隔离模式)                18.5 ms   启动后 sys.modules 有 54 个
  python -I -S -c pass                     12.9 ms   启动后 sys.modules 有 21 个
  python -E -c pass  (忽略环境变量)              18.9 ms   启动后 sys.modules 有 54 个

-S 让 site 不再导入:启动从 18.8 ms 降到 12.8 ms,启动后的 sys.modules 从 54 个降到 21 个。-I(隔离模式)几乎没有变快,模块数也没变——它并没有跳过 site,只是让 site 忽略环境变量和用户目录。-E 只忽略环境变量。想要「最小启动」需要 -S,想要「不受环境影响」用 -I,两者不是一回事。

验证三:site 阶段会执行什么

.pth 文件与 sitecustomize
实测 · 本机执行输出
  在用户 site-packages 里放 demo.pth, 在 PYTHONPATH 里放 sitecustomize.py, 然后运行:
  [.pth] demo.pth 的这一行在启动时执行了
  [sitecustomize] 被自动导入了
  [脚本] 主脚本开始执行
  extra_path 在 sys.path 里? True
  同样的环境, 加 -S:
  [脚本] 主脚本开始执行
  extra_path 在 sys.path 里? False
  同样的环境, 加 -I:
  [脚本] 主脚本开始执行
  extra_path 在 sys.path 里? False

我在用户 site-packages 里放了一个 demo.pth:一行以 import 开头(会被当作代码执行),一行是路径(会被加进 sys.path);在 PYTHONPATH 指定的目录里放了 sitecustomize.py。运行结果显示,它们都在「主脚本开始执行」之前打印,路径也确实进了 sys.path。加 -S 或 -I 后两者都没有执行。

这是 Python 能在启动时自动加载东西的两个入口(许多工具靠它注入行为),也是排查「为什么我的脚本没运行就有奇怪行为」时该看的地方:site-packages 里的 *.pth 和 sitecustomize/usercustomize。

验证四:sys.argv 与 sys.orig_argv

实测 · 本机执行输出
  orig_argv = ['-I', '-c', 'import sys; print("orig_argv =", sys.orig_argv[1:], "| argv =", sys.argv, "| isolated =", sys.flags.isolated, "| no_user_site =", sys.flags.no_user_site, "| ignore_environment =", sys.flags.ignore_environment)'] | argv = ['-c'] | isolated = 1 | no_user_site = 1 | ignore_environment = 1

sys.orig_argv 保留完整的原始命令行(包括给解释器的 -I、-c),sys.argv 只是交给脚本自己的那部分。-I 同时打开了 isolated、no_user_site、ignore_environment 三个标志。

分步演示:从回车到第一行代码

模块顺序与耗时来自 -X importtime 的实测输出
Python 解释器启动的四个阶段
此刻的阶段 / 数据

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7(Homebrew),每种开关取 30 次中位数;-X importtime 的耗时随机器负载有波动,本次绝对值仅供参考
    • 没有验证:虚拟环境下 site 的处理差异、frozen modules 开关(-X frozen_modules)的影响、Windows 上的启动路径、编译成字节码缓存后的冷启动
    京ICP备2026067502号-1京公网安备11010502063487号