
Python 3.15.0 在 10 月 9 日正式发布了(见官方发布说明)。前天那篇讲了"要不要升级"的 6 个变化(回看),今天这篇只干一件事:把头牌特性 lazy import 拿出来实测一遍——它是噱头还是真有用,数据说了算。
先一句话说清它是什么:3.15 新增了一个软关键字 lazy(PEP 810),lazy import x 只声明、不加载,第一次真正用到这个模块时才执行导入。对"启动一次、偶尔才用重库"的脚本来说,这就是天生的加速器。
先复现一个痛点:--help 也要等 2 秒
很多人都写过这种脚本:顶层 import 一堆库(数据分析、图像处理、网络请求……),结果用户敲 --help 看个用法说明,也要等所有库加载完才有输出。下面这个实验模拟的就是这种情况:
# heavy_mod.py:模拟一个 import 成本很高的重库(约 1.5 秒,numpy/torch 级别)
import time
time.sleep(1.5)
def run():
return "heavy work done"
# demo_eager.py:传统写法,顶层直接导入
import argparse
import heavy_mod
p = argparse.ArgumentParser()
p.add_argument("--work", action="store_true")
args = p.parse_args()
if args.work:
print(heavy_mod.run())
# demo_lazy.py:3.15 写法,只加一个词
import argparse
lazy import heavy_mod # 声明但不加载
p = argparse.ArgumentParser()
p.add_argument("--work", action="store_true")
args = p.parse_args()
if args.work:
print(heavy_mod.run())
改动真的只有三行:把 import 换成 lazy import。实测结果(Python 3.15.0,time 命令测整进程耗时):
| 场景 | 普通 import | lazy import |
|---|---|---|
--help(没用到模块) | 1.75 秒 | 0.075 秒 |
--work(真用到了模块) | 1.55 秒 | 1.54 秒 |
结论很干净:没用到的模块,一分钱加载成本都不付;用到了的分支,速度和以前一模一样——延迟加载没有惩罚项。
声明不等于加载:验证一下是真的"懒"
眼见为实,用 sys.modules 检查模块到底什么时候进内存:
import sys
lazy import heavy_mod
print("声明后在吗:", "heavy_mod" in sys.modules) # False,还没加载
run = heavy_mod.run # 第一次访问属性
print("第一次使用后在吗:", "heavy_mod" in sys.modules) # True,触发加载
输出确认了:lazy import 之后模块不在 sys.modules 里,第一次访问它的属性时才真正导入。三写法都支持,实测全部通过:
lazy import heavy_mod # 基础写法
lazy from heavy_mod import run # from 写法
lazy import pkg.sub as s # 子模块 as 写法(正式版前修过的 bug,rc3 已解决)
哪些地方该改,哪些别乱动
该改的,特征就一个:"重、但只有少数分支才用到"的库。
- CLI 工具的
--help/--version/ 参数校验分支:这些路径根本用不到重库,改完秒回,用户体验提升最明显。 - 定时脚本、运维小工具:每天跑一次,启动快 1 秒也是赚。
- Agent / 自动化工具:
--help被频繁调用的场景,累积收益可观。
别乱动的也有三类:
- import 有副作用的库别偷懒——有些库在导入时做插件注册、打 monkey patch,延迟加载会改变副作用发生的时机,行为可能变。
- 主流程第一行就用的库:改了也没收益,还多一层心智负担。
- 还在用 3.14 及更早版本跑的项目:
lazy语法在旧版本直接报 SyntaxError,要升级解释器再动手。正式版刚发布两天,第三方库跟进中,生产环境建议等 3.15.1(和前天那篇的建议一致)。
另外一个好消息:lazy 是软关键字,你旧代码里要是有个变量就叫 lazy,不会被破坏——这是 PEP 810 设计时就保证的。
和"老办法"比,好在哪
以前想达到同样效果,常见做法是把 import 塞进函数里:
def work():
import heavy_mod # 延迟到调用时
return heavy_mod.run()
这招能用,但代价是每个用到的地方都要重复写、静态分析工具也看不懂你的依赖。lazy import 是语言级特性:顶层声明一次,用法和普通导入一模一样,IDE 跳转、lint 检查都不受影响。写法干净,才是它值得升级的最大理由。
一句话总结:Python 3.15 的 lazy import 不是发布会噱头,是实打实的"启动加速器"。把你脚本里"重但不常用"的 import 前面加个 lazy,--help 秒回的那种爽快,试一次就回不去了。
相关阅读:Python 3.15 明天正式发布:这 6 个变化,决定你要不要升级 · PEP 810 – Explicit lazy imports · What's New in Python 3.15
