Python 3.15新增Lazy Import语法 削减启动耗时并清理冗余代码

【python】lazy import详解,3.15新特性它来啦! — 码农高天

码农高天

CPython通过代理对象将模块执行延迟至属性调用时刻,使开发者无需在函数内编写局部导入或类型守卫即可消除重型库的冷启动性能损耗。

AI-assisted summary of 【python】lazy import详解,3.15新特性它来啦! — original source. Check the original for full context.

Source published:

Key ideas

  • Python 3.15 完成特性冻结,正式引入备受关注的语法新特性 lazy import(如 `lazy import json`),仅在真正访问模块属性时才执行模块代码。 (0:00)
  • Lazy import 旨在解决两大工程痛点:消除为了规避冷启动耗时而在多个函数内编写重复局部导入的妥协,以及简化基于 `TYPE_CHECKING` 的类型注解冗余写法。 (1:43)
  • 底层通过 `IMPORT_NAME` 字节码的操作数标志位生成空壳代理对象(proxy),并提供全局策略控制(`normal`、`all`、`none`)以应对第三方库调优。 (3:22)
  • Lazy import 受到严格语法限制:必须写在模块最顶层,禁止置于函数、类定义或 `try...except` 块中,且有副作用的模块不适用。 (5:00)

Detailed notes

## Background 该视频由 YouTube 频道“码农高天”于 2026 年 5 月 23 日发布。视频简介指出,本期内容深入介绍了 Python 3.15 刚完成特性冻结(feature freeze)后带来的重磅全新语法 lazy import(惰性导入),认为几乎每一位 Python 开发者都值得了解这项改动。

## TL;DR 视频全面解析了 Python 3.15 新增的 `lazy import` 语法特性。该特性通过推迟模块代码的执行时机,能够优雅替代以往为了启动性能而在大量函数内编写的局部导入(local import),并消除类型注解中冗长的 `if TYPE_CHECKING:` 模板代码。高天进一步通过字节码剖析了其底层构建 Proxy 空壳模块的实现机制与全局策略覆盖接口,最后盘点了该特性只能在顶层声明、不能放进 `try...except` 块以及延迟报错等使用限制与避坑指南。

## Atmosphere & style 整期视频节奏干练利落、逻辑层层递进,延续了高天频道一贯扎实硬核的“源码+字节码”深度解析风格。从直观的运行顺序 demo,到 PySpark 真实工业级源码的痛点映照,再深入到底层解释器实现,兼具技术探索的兴奋感与务实落地的说服力。讲解不仅聚焦新特性的便利与惊艳,更在结尾冷静且严肃地划清语法边界与踩坑场景,让技术分享显得格外客观严谨。

## Bulletin - [00:00](https://www.youtube.com/watch?v=UYdxbADcYEw&t=0s) Python 3.15 完成特性冻结,正式引入备受关注的语法新特性 lazy import(如 `lazy import json`),仅在真正访问模块属性时才执行模块代码。 - [01:43](https://www.youtube.com/watch?v=UYdxbADcYEw&t=103s) Lazy import 旨在解决两大工程痛点:消除为了规避冷启动耗时而在多个函数内编写重复局部导入的妥协,以及简化基于 `TYPE_CHECKING` 的类型注解冗余写法。 - [03:22](https://www.youtube.com/watch?v=UYdxbADcYEw&t=202s) 底层通过 `IMPORT_NAME` 字节码的操作数标志位生成空壳代理对象(proxy),并提供全局策略控制(`normal`、`all`、`none`)以应对第三方库调优。 - [05:00](https://www.youtube.com/watch?v=UYdxbADcYEw&t=300s) Lazy import 受到严格语法限制:必须写在模块最顶层,禁止置于函数、类定义或 `try...except` 块中,且有副作用的模块不适用。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=UYdxbADcYEw&t=0s) Python 3.15 进入特性冻结阶段,其中 lazy import 是近年来少有的呼声极高、且在语法层面对开发者日常编写产生直接影响的新特性。 - [00:20](https://www.youtube.com/watch?v=UYdxbADcYEw&t=20s) Lazy import 语法极为直观,支持 `lazy import ...` 与 `lazy from ... import ...` 两种主要书写格式。 - [00:50](https://www.youtube.com/watch?v=UYdxbADcYEw&t=50s) Lazy import 会将目标模块内部代码的执行延迟到第一次真正访问其导出函数或属性之前,从而避免启动时立即加载带来的耗时。 - [01:43](https://www.youtube.com/watch?v=UYdxbADcYEw&t=103s) 大型第三方库(如 PyArrow、Pandas)的导入常耗费数百毫秒,PySpark 等工程此前被迫在几十个函数内分散编写局部导入以换取启动速度。 - [02:20](https://www.youtube.com/watch?v=UYdxbADcYEw&t=140s) 模块顶层的 lazy import 可以兼顾启动性能与代码整洁度,同时让静态分析工具获得完整准确的顶层依赖信息。 - [02:45](https://www.youtube.com/watch?v=UYdxbADcYEw&t=165s) Lazy import 可直接替代类型检查中冗长的 `if TYPE_CHECKING:` 保护块,使 Mypy 等类型检查器正常解析类型而不在运行时产生加载开销。 - [03:22](https://www.youtube.com/watch?v=UYdxbADcYEw&t=202s) 在字节码层面,普通导入与惰性导入共用 `IMPORT_NAME` 指令,仅通过传入的操作数标志位区分,决定调用常规导入还是 `lazy_import_name`。 - [04:00](https://www.youtube.com/watch?v=UYdxbADcYEw&t=240s) Lazy import 的核心机制是先创建一个空壳代理对象(proxy module object),直至实际访问属性时才反向触发真实模块的执行。 - [04:25](https://www.youtube.com/watch?v=UYdxbADcYEw&t=265s) CPython 提供了全局策略覆盖机制(支持命令行、环境变量及 `sys.set_lazy_imports`),包含 `normal`、`all` 和 `none` 三种行为模式。 - [05:00](https://www.youtube.com/watch?v=UYdxbADcYEw&t=300s) 依赖导入副作用(Side Effects)或立即初始化逻辑的模块不能使用 lazy import,否则会导致程序状态出现未预期时序差。 - [05:15](https://www.youtube.com/watch?v=UYdxbADcYEw&t=315s) 由于模块实际加载被推迟,导入语句中的拼写错误或异常不会在启动时立即抛出,而是延迟至首次访问属性时才触发。 - [05:30](https://www.youtube.com/watch?v=UYdxbADcYEw&t=330s) Lazy import 在语法上仅支持模块最外层(Module Level),若置于函数内、类定义内或 `try...except` 块中,在编译期就会直接报错。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=UYdxbADcYEw&t=0s) Python 3.15 新特性 Lazy Import 的基本语法与行为表现

Python 3.15 在前几天已经完成了 feature freeze(特性冻结)。在这一版本中,大家关注度最高的新特性莫过于 **lazy import(惰性导入)**。一方面是因为近年来 Python 在语法层面上的改动确实较少;另一方面,lazy import 本身呼声极高,并且能给绝大多数开发者带来切实的帮助。虽然 3.15 正式版尚未发布,但高天在这期视频中提前带大家一睹该特性的具体样貌。

在语法上,lazy import 的写法非常直观:直接在原本正常的导入语句前加上 `lazy` 关键字(如 `lazy import json`)。由于当时 VS Code 的 Linter 尚未支持 Python 3.15,编辑器内会对其报红不识别,但实际运行起来与普通导入在功能上是一致的。为了展示它究竟“lazy”在哪里,高天以一个自定义的 `slow.py` 为例:该模块在被导入时会打印一行“该模块已被 import”,同时内部还定义了一个 `slow_function()`。

在传统导入方式与 lazy import 之间存在明显的执行顺序差异: - **普通导入(`import slow`)**:程序启动执行到该行时,会立即加载模块,控制台先打出“module 被 import”,接着按顺序打印主程序的 `hello`、调用 `slow_function()`、再打印 `world`。 - **惰性导入(`lazy import slow`)**:终端最先输出的却是 `hello`。这意味着执行到 `lazy import` 这一行代码时,根本没有去执行 `slow` 模块内部的代码;那行“`slow.py is imported`”一直被推迟到后续真正调用 `slow_function()` 之前才触发执行。

高天总结道:“那这个就是所谓 lazy import,就是当我真正需要这个 module 的时候再去 import 它。”此外,lazy import 同样支持 `lazy from... import...` 这种形式,其行为一致,只有在真正需要访问导出函数或对象时,才会运行目标模块的代码。

---

[01:43](https://www.youtube.com/watch?v=UYdxbADcYEw&t=103s) 解决的两大痛点:函数内 Local Import 与 Type Checking 冗余代码

Lazy import 之所以拥有如此高的呼声,主要是因为它能干净地解决日常 Python 开发中非常典型的两个痛点:

```mermaid flowchart TD accTitle: Lazy import 解决的典型问题 accDescr: 展示 lazy import 如何取代旧有的本地局部导入与 TYPE_CHECKING 保护块写法

subgraph Pain1["痛点一:为了启动性能而做的局部导入"] A1["模块如 PyArrow / Pandas 耗时几百毫秒"] --> B1["旧做法:在 10~20 个函数内分别写 local import"] B1 --> C1["新做法:模块顶部直接 lazy import,兼顾性能与代码整洁"] end

subgraph Pain2["痛点二:类型注解导致的运行时代价"] A2["Type Checker 需要知道类型来源"] --> B2["旧做法:写 if TYPE_CHECKING: 守卫避免运行时加载"] B2 --> C2["新做法:顶层 lazy import,类型检查可识别且不拖慢 Runtime"] end ```

第一个痛点是**为了避免启动性能损耗而到处编写局部导入(local import)**。高天以 PySpark 的源码为例,展示了其库中存在大量在函数内部局部导入 `pyarrow` 和 `pandas` 的写法。之所以这么做,最核心的原因就是性能:这些大型第三方库的 import 耗时普遍偏长,“可能这一行代码几百毫秒就没了”。如果某些执行路径根本用不上它们,每次启动程序白白耗费几百毫秒是非常不划算的,开发者被迫写成“按需 import”。但带来的代价就是代码极度啰嗦,十几个甚至二十几个函数中都在重复写相同的 import。有了 lazy import 之后,开发者可以直接在文件最顶部写 `lazy import pyarrow as pa`;只要程序运行时用不到,就不会产生任何性能开销,既保持了代码整洁,也让静态分析工具的分析更为准确。

第二个痛点是**类型注解(Type Annotation)的条件导入**。静态类型检查工具(如 Mypy)在分析代码时必须明确类型定义从何而来,以往的标准做法是编写冗长的条件保护块: ```python if TYPE_CHECKING: from some_heavy_module import SomeType ``` 这种写法既啰嗦又影响阅读。引入 lazy import 之后,开发者可以直接在模块顶部进行 lazy import,并在类型注解处正常书写类型标记。类型检查器可以顺利找到对应类型,同时该导入在真正的运行时(Runtime)不会产生任何负担。

---

[03:22](https://www.youtube.com/watch?v=UYdxbADcYEw&t=202s) 底层原理与 Global Policy 全局行为控制

按照频道的“传统异能”,高天进一步通过字节码(bytecode)深入剖析了 lazy import 的底层实现机制。对比普通代码与 lazy import 代码的编译结果可以发现: - 普通 `import json` 对应的核心字节码为 `IMPORT_NAME 0 (json)`; - 改为 `lazy import json` 后,字节码变成了 `IMPORT_NAME 1 (json + LAZY)`。

在 CPython 底层的 `IMPORT_NAME` 字节码处理中,解释器会检查传入的 `oparg` 操作数。如果带有 lazy 相关的 flag,就会调用 `lazy_import_name`,否则走常规的 `import_name`。也就是说,普通 import 与 lazy import 在编译后对应的是同一条字节码指令,只是传入的参数标志位不同,解释器据此决定具体如何加载。

当决定采用 lazy import 时,CPython 会生成一个 **lazy import module object**。高天解释道:“它本质上就是一个 proxy 啊,就是先给你建个空壳,然后什么时候你真正需要他里面的 attribute 了,他再去运行那个 module 代码,然后反馈给你。”

此外,CPython 还提供了一套 **Global Policy Override(全局策略覆盖)** 机制。开发者不仅能通过传统的命令行参数、环境变量进行调整,还可以在代码中通过 `sys.set_lazy_imports(...)` 接口在全局范围内控制导入行为。该策略支持三种模式: - **`normal`(默认)**:仅有显式写了 `lazy` 的导入语句才会以惰性方式加载; - **`all`**:全局所有模块层级(module-level)的导入操作都会自动变成 lazy import; - **`none`**:完全禁用 lazy 特性,即使代码中显式写了 `lazy import`,也会退化为普通 import 照常立即加载。

高天指出,这种全局开关对于应用程序控制自身依赖的第三方库行为十分有用,“当然人家也说了这是 advanced feature,一般人用不着”。

---

[05:00](https://www.youtube.com/watch?v=UYdxbADcYEw&t=300s) 使用限制与常见踩坑点

在视频最后,高天盘点了不能直接加 `lazy` 的使用限制与潜在踩坑点,提醒开发者在正式上手时需特别注意:

- **依赖副作用(Side Effects)的模块不可使用**:如果一个模块在导入时包含某些必须立即执行的初始化逻辑或副作用代码,就不能使用 lazy import。 - **语法错误和导入异常会被延迟抛出**:由于模块真正执行被推迟,写在导入语句上的拼写错误(例如将 `loads` 拼错)在程序刚启动时不会立刻报错,必须等到代码首次尝试使用该属性时才被抛出。 - **仅支持在模块顶层(Module Level)使用**:Lazy import 只能写在模块最外层,禁止写在函数内部或 class 定义内部,否则在编译阶段(compile time)就会直接报错。 - **禁止置于 `try...except` 块中**:不能将 `lazy import` 放在 `try` 块内尝试捕获导入异常,否则同样会在编译期报错。

针对这一特性的前景,高天给出了高度评价:“lazy import 这个东西是我觉得这些年 Python 比较少见的让大家都会想去尝试的新特性。”他鼓励大家在 Python 3.15 正式版发布后尽快体验。

## Notable quotes > [05:00](https://www.youtube.com/watch?v=UYdxbADcYEw&t=300s) lazy import 这个东西是我觉得这些年 Python 比较少见的让大家都会想去尝试的新特性。 — 高天

## Mentioned Python, CPython, 高天, VS Code, PySpark, PyArrow, Pandas, Mypy

## Video description 这期视频来介绍一下3.15的一个新语法,lazy import。几乎每一个用python的人都值得了解一下它!

[Source](https://www.youtube.com/watch?v=UYdxbADcYEw)

中文简报

Python 3.15新增Lazy Import语法 削减启动耗时并清理冗余代码 CPython通过代理对象将模块执行延迟至属性调用时刻,使开发者无需在函数内编写局部导入或类型守卫即可消除重型库的冷启动性能损耗。 视频全面解析了 Python 3.15 新增的 `lazy import` 语法特性。该特性通过推迟模块代码的执行时机,能够优雅替代以往为了启动性能而在大量函数内编写的局部导入(local import),并消除类型注解中冗长的 `if TYPE_CHECKING:` 模板代码。高天进一步通过字节码剖析了其底层构建 Proxy 空壳模块的实现机制与全局策略覆盖接口,最后盘点了该特性只能在顶层声明、不能放进 `try...except` 块以及延迟报错等使用限制与避坑指南。 Python 3.15 完成特性冻结,正式引入备受关注的语法新特性 lazy import(如 `lazy import json`),仅在真正访问模块属性时才执行模块代码。 (0:00) Lazy import 旨在解决两大工程痛点:消除为了规避冷启动耗时而在多个函数内编写重复局部导入的妥协,以及简化基于 `TYPE_CHECKING` 的类型注解冗余写法。 (1:43) 底层通过 `IMPORT_NAME` 字节码的操作数标志位生成空壳代理对象(proxy),并提供全局策略控制(`normal`、`all`、`none`)以应对第三方库调优。 (3:22) Lazy import 受到严格语法限制:必须写在模块最顶层,禁止置于函数、类定义或 `try...except` 块中,且有副作用的模块不适用。 (5:00)

Reading summary

Python 3.15 引入了备受瞩目的 lazy import(惰性导入)新特性。博主码农高天指出,该语法通过将模块代码的执行推迟至首次访问属性时,不仅能显著优化重型模块的启动性能并简化类型注解代码,还提供了底层的代理对象与全局控制策略,但开发者需注意其在副作用处理和语法作用域上的严格限制。 惰性导入的语法表现与执行机制 (0:00) 在 Python 3.15 中,开发者只需在常规导入语句前添加 `lazy` 关键字(如 `lazy import json` 或 `lazy from... import...`)。传统导入会在执行到该行时立即解析并执行目标模块代码;而 lazy import 在被声明时完全不执行目标模块内部逻辑,仅在后续代码真正调用模块属性或导出函数之前,才会触发模块代码的运行。由于该特性刚完成特性冻结(feature freeze),早期编辑器插件可能会将其识别为语法错误,但运行时行为与普通导入在功能层面保持一致。 化解局部导入与类型检查的代码冗余 (1:43) Lazy import 重点解决了日常开发中的两大典型痛点。首先是消除为了性能而在函数内部编写的大量局部导入(local import):如 PySpark 等项目中,因 PyArrow、Pandas 等重型库导入耗时可达数百毫秒,开发者常被迫在十几个函数中重复局部导入以避免无谓的启动开销;使用 `lazy import` 后可直接统一写在模块顶部,既不拖累启动耗时,又维持了代码整洁。其次是简化了静态类型检查,以往必须通过 `if TYPE_CHECKING:` 保护块来避免运行时加载沉重模块,现在直接在顶层声明 lazy import 即可被类型分析器捕获,且运行时零开销。 字节码实现与全局策略覆盖机制 (3:22) 从底层实现来看,普通导入编译为字节码 `IMPORT_NAME 0`,而 `lazy import` 编译为带有标记位的 `IMPORT_NAME 1 (name + LAZY)`,解释器据此在 CPython 层面分流调用 `lazy_import_name`。其本质是先生成一个代理模块对象(lazy import module object / 空壳),在检测到属性访问时再加载真实模块。此外,CPython 还提供了全局覆盖机制(Global Policy Override),允许通过命令行、环境变量或 `sys.set_lazy_imports(...)` API 设置三种运行模式:默认的 `normal`(仅生效显式 lazy 语句)、`all`(全局所有模块层级导入自动转为 lazy)以及 `none`(强制禁用 lazy 特性,全部立即加载)。 使用限制与异常延迟暴露风险 (5:00) Lazy import 并不适用于所有场景,存在明确的使用边界与排错成本。首先,依赖导入副作用(如立即执行全局初始化)的模块不能使用惰性导入;其次,拼写错误等异常会被推迟到属性被实际使用时才抛出,增加了错误排查的滞后性。在语法约束方面,lazy import 仅支持在模块顶层(Module Level)声明,禁止写在函数或类定义内部;同时,语法上严格禁止将其包裹在 `try...except` 块中捕获导入异常,上述违规写法均会在编译阶段直接报错。 高天认为 lazy import 是近年来 Python 语法层面最具实用价值的新特性之一。尽管其延迟报错与严格的顶层语法约束需要开发者调整编写习惯,但在正式版发布后,该特性预计将在优化大型项目冷启动耗时和代码规范性上发挥重要作用。

Source quotes

[5:00] 高天: lazy import 这个东西是我觉得这些年 Python 比较少见的让大家都会想去尝试的新特性。

Original transcript

[0:00] python3.15已经在前几天被他freeze了这个版本里大家比较关注的新特性可能就是lazy import第一是因为最近这些年Python语法层面上的改动确实比较少第二是Lazy Import这个东西本身确实是呼声很高 而且对大部分人都有帮助那么今天这期视频就来做一个前瞻啊虽然3.15正式版还没出但是我们看一下这个功能是什么样的那这个lazy import大概是长成这个样子的可以看到VSCode的Linter还没开始支持3.15啊所以他不认识这个东西我们在正常的import JSON 之前加一个 lazy可以看到效果和我们impro.json是一样的那他lazy在哪呢我们换个例子我们假设我们有一个slow.py这个slow.py在import的时候会打印一个它被import了同时它里面还定义了一个function我们把terminal拉到右边来如果我们是正常的import slow这里它就会先打出来这个module被import然后是hello,然后是slow function,然后是world但如果我们使用的是LazyImport可以看到我们先打出来的是hello 也就是说lazyimport这行并没有执行slow这个module里面的代码这个slow.py is imported是在我们使用这个slow function之前才执行的那这个就是所谓lazy import就是当我真正需要这个module的时候再去import它那LazyImport还支持这种fromImport的形式可以看到运行结果依然是在需要用到这个slow function的时候他才真正运行了这个slow module的代码好 那这个东西有什么用或者说为什么这个功能的呼声很高啊这么多人想叫这里有两个非常常见的问题可以通过lazyimport解决我们看这是PySpark的代码这种localimport大家肯定特别不陌生那PySpark里面有大量的这种在函数内部对PyArrow和Pandas进行import的代码我们可以随便找找 可以看到还是非常多的之所以要这么干 最大的原因就是性能因为不管是Error还是Pandas 它们Import的时间都偏长可能这一行代码几百行秒就没了如果我的程序压根用不着这两个东西是不是每一次启动的时候都要花费这好几百毫秒于是我就只能把这个部分变成按需import就你需要用到的时候你再import但是随之而来的问题是我在很多很多函数都要用到于是就形成了现在这样的结果我可能有十几个二十个这样的import在不同的函数里那如果有了lazy import 我不用管这件事了我就可以直接在最上面lazy import py arrow SPA如果我用不到它 它不会对我的性能有任何影响这样可以让我的代码更加整洁同时可以让静态分析工具分析的更准同样一个文件 它其实还展示了第二点lazy import可以解决的问题我们有的时候会希望对函数进行type annotation但是你的type checker 不管你是用mypy还是什么它必须要知道你这个类型是从哪来的那我们就只能写一个iftype checking在import这些如果买派在检查的时候把这些import了如果我是正常的运行的话就不import这个写的也是非常啰嗦在以后我们依然可以直接勒进import然后在annotation的地方再该写什么写什么让type checker自己去找相应的类型从而不对我真正的run time有任何的影响接下来我们频道的传统异能这个功能它是怎么实现的呢我们看一下左边这段程序对应的bytecode它对应的核心代码是impro name0 JSON而当我们把左边的代码变成lazy import之后刚才的那行 import name就变成了 import name 1 JSON+LAZY在 import name这个 bytecode里它会检查一下这个up arg如果这个flag没有被set它就使用lazy import name否则的话就是下面的import name也就是说lacyimport和import他们在被compile的时候会变成同一个bytecode但是有不同的参数通过这个参数Python的Interpreter再来决定如何去import这个东西那如果决定是lazyimport了就会建立这么一个lazyimport module object它本质上就是一个proxy啊就是先给你建个空壳 然后什么时候你真正需要他里面的attribute了 他再去运行那个module代码 然后反馈给你这个东西不是很复杂啊我现在大家自己都会写但是如果我们把这个函数往上拉一点你会发现这里有一个所谓的global policy override那这里其实是对应着几种方式让你可以在全局控制这个lazy import的behavior控制的方式也是cpagel很传统的你可以通过 command line 可以通过 invariant variable还可以直接sys set please imports它支持三种模式啊默认就是normal也就是只有你写lazy的import才是lazy的除了normal之外你还可以用all也就是所有的module level imports都会变成lazy的那除了all之外当然还有none也就是哪怕你写了lazy我也不lazy我就照常import这个对于一些应用来控制自己使用的第三方酷的一些行为还是有帮助的当然人家也说了这是advanced feature一般人用的 [5:00] 最后我们来介绍一些lazy import可能掉进去的小坑也就是什么情况下你不能直接在那个import前面加个lazy首先最直观的当你需要side effect的时候就是我这module里面有一些代码我需要它在这个时候执行那你当然不能lazy import然后你要意识到在import这行出现的错误有可能会被延迟比如我这块打错字了本来应该是load子我打错了这个时候运行这个程序是不会报错的因为它是lazy import它lazy了它还没来机import呢然后lazy import只可以在module level进行lazy import不能放到函数里面和class的定义里面否则在compile的时候就会出错另外lazy import也不能放到tri block里否则也是compile的时候出错lazy import这个东西是我觉得这些年python比较少见的让大家都会想去尝试的新特性等到3.15正式版出来之后大家可以尽快上车感受一下好了 今天视频就到这里啊喜欢的话记得关注 别忘了一件三零哦