Python 3.15 lazy import 实战:--help 从 1.7 秒降到 0.07 秒

Python 3.15 lazy import 实战:--help 从 1.7 秒降到 0.07 秒

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 命令测整进程耗时):

场景普通 importlazy 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 被频繁调用的场景,累积收益可观。

别乱动的也有三类:

  1. import 有副作用的库别偷懒——有些库在导入时做插件注册、打 monkey patch,延迟加载会改变副作用发生的时机,行为可能变。
  2. 主流程第一行就用的库:改了也没收益,还多一层心智负担。
  3. 还在用 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