
Python 3.15.0 的第三个发布候选版本,也就是 rc3,已于2026年10月2日发布。正式版计划在10月9日推出。
仅从 rc2 到 rc3,82位贡献者就提交了大约156项错误修复。发布管理团队也已经收紧入口:只有经过审查的错误修复,才能继续进入这个版本。
因此,从实际使用角度来说,rc3 里看到的内容,基本就是正式版的样子。
于是,新一轮热闹又开始了。每次 Python 发布,都少不了“这次改变了一切”的文章。今年,还得加上一个 AI 角度。
3.15 确实包含一些值得关注的功能,而且正好对应2026年大家经常写的 Python:每次请求都要启动的智能体工具服务、导入一大堆 PyPI 包的 notebook、结构化输出解析器,以及同时发起十次 LLM 调用、最终只需要一个结果的异步编排代码。
智能体开发者绕了多年,终于等来的功能
PEP 810 引入了一个软关键字:lazy。它可以把模块加载推迟到第一次真正使用的时候。
如果你觉得这个思路眼熟,没错,它以前就被尝试过。
2022年的 PEP 690,曾提议把惰性导入做成一个全局开关。但 Python 指导委员会没有接受,主要原因是:一次性改变整个程序里所有导入的语义,可能以难以预料的方式破坏现有代码。
由 Pablo Galindo Salgado、Germán Méndez Bravo、Thomas Wouters、Dino Viehland 等人参与的 PEP 810,则选择了另一条路线:显式声明。
你可以一行一行地决定,哪些导入采用惰性加载。
真正让我注意到它的,是 PEP 中引用的数据:在实际命令行工具中,启动性能可改善约50%—70%;在真实工作负载中,内存占用可减少约30%—40%。
它还提到,标准库中大约17%的导入,已经被开发者手动塞进函数内部,目的就是避开启动成本。
Python 开发者写惰性导入,已经写了很多年。只是以前,得把代码写得有点别扭。
为什么到了2026年,这件事更重要?
想想现在很多 AI 工具的运行方式。通过 stdio 通信的 MCP 服务器,往往是客户端会话开始时启动的子进程。编程智能体完成一个任务,也可能几十次调用 linter、格式化工具、测试程序和小型辅助脚本。
封装模型调用的无服务器函数,每次冷启动也要支付导入成本。在这些场景中,进程可能只运行几秒,而其中相当一部分时间,却花在导入 torch、transformers 或 pandas 上。偏偏当前执行路径,可能根本用不到它们。
以前的办法,是把导入语句搬进需要它的函数。确实有效,但依赖因此散落在文件各处,阅读时不容易一眼看清,还可能让检查工具不断提醒。到了3.15,你可以继续把导入放在模块顶部,只给那些重量级模块标记惰性加载。
对于使用类型标注的代码库,还有一个不那么显眼的收益。PEP 指出,惰性导入可以减少不少 if TYPE_CHECKING: 保护代码的需求,因为只用于注解、运行时又没有实际求值的导入,不必立即加载。
这些规则,最好提前知道
惰性导入只允许出现在模块作用域。不能放进函数、类或者 try 块,也不能与星号导入、__future__ 导入组合使用。
如果惰性导入失败,异常信息会关联导入声明的位置,以及第一次使用它的位置。程序运行二十分钟后才触发导入错误时,这一点尤其重要。
需要兼容旧版 Python 的库,也有相应的兼容方式:
如果想从运行环境层面尝试,还可以使用 -X lazy_imports、PYTHON_LAZY_IMPORTS 环境变量,以及 sys.set_lazy_imports_filter(),决定哪些模块符合条件。按 PEP 的说明,自适应解释器会在首次使用后对查找进行专门优化,因此稳定运行阶段的额外成本基本可以忽略。
说到底,Python 开发者早就在做惰性导入。现在,终于不用再为它绕那么多弯了。
02/Tachyon
程序已经在跑,也能接上去分析
PEP 799 把 Python 的性能分析工具重新组织到新的 profiling 包中。确定性跟踪放在 profiling.tracing 下,cProfile 保留为兼容别名;旧的纯 Python profile 模块则被弃用,计划在3.17移除。
其中最值得关注的,是昵称为 Tachyon 的 profiling.sampling。这是一个统计采样分析器,支持最高1,000,000 Hz 的采样频率,还可以通过 PID 附加到正在运行的进程,不必重启程序。
它支持墙钟时间、CPU、GIL 持有状态和异常等分析模式,可以输出 pstats、折叠堆栈、火焰图、Firefox Profiler 格式和热力图。它能识别线程,也能按异步任务重建调用栈。最后这一点,是我最关心的。
原因很简单。LLM 应用大量使用异步代码,而且大多数时候受 I/O 限制。智能体循环感觉很慢时,真正的答案通常是“它在等”。有用的问题则是:“到底在等什么?”确定性跟踪工具引入的开销,可能影响你正在观察的时间表现,而协程又让分析变得更麻烦。
一个开销较低、能理解异步任务,还能在 worker 卡住之后直接接入的采样工具,正是很多 AI 后端团队想要的东西。
另一个相关变化来自更底层。PEP 831 让 CPython 默认启用帧指针,帮助原生调用栈更快地展开,也让系统性能分析器、调试器和 eBPF 工具更清楚地观察 Python 进程。平时你可能感觉不到,真要排查问题时,它就有用了。
03/两个新的内置工具
frozendict和sentinel,不必再到处找替代品
Victor Stinner 和 Donghee Na 提出的 PEP 814,把 frozendict 加入内置类型。它不可变;当其中的键和值都可哈希时,它本身也可以哈希。
有几个设计细节值得记住。它不是 dict 的子类,而是直接继承 object,避免从父类继承可能修改数据的方法。它保留插入顺序,但相等性判断和哈希不依赖顺序。
| 运算符会返回新的 frozendict,|= 则重新绑定变量,而不是原地修改。在 CPython 中,浅拷贝可以直接返回同一个对象,因此这一步是 O(1)。它也与 copy、json、pickle 和 pprint 配合使用。
实际收益,在缓存场景里很容易看到。今天,把一个包含生成参数的 dict 传进使用 functools.lru_cache 的函数,就可能得到 TypeError,因为普通字典不可哈希。
而把 {"model": ..., "temperature": 0.2, "max_tokens": 512} 这样的参数放进 frozendict,在成员可哈希的前提下,就能用作缓存键。不可变配置、不会被意外修改的函数默认值,以及由结构化数据构成的字典键,也都能受益。
另一个,是 PEP 661。它增加了内置的 sentinel,用来处理那个经典需求:“我需要一个表示缺失的值,但它不能是 None。”
sentinel 在复制时保持身份,有清楚的 repr;当它能通过模块和名称导入时,也支持 pickle,并能在类型表达式中使用 |。如果你写过 _MISSING = object(),又被异常信息里难看的对象表示烦到过,这个功能就是为你准备的。
在 LLM 代码中,它还有一个很具体的用途:模型把某个字段返回为 null,和模型根本没返回这个字段,是两种不同情况。很多时候,你必须分清它们。
04/推导式解包
一个小语法,能省掉不少展开代码
PEP 798 允许在推导式和生成器表达式中使用 *、** 解包。
听起来很小,但看看检索代码里有多少地方在展开数据:每个文档的 chunks、每条消息的工具调用、每个答案的引用。
以前,你要么写嵌套推导式,让同事多看两眼才能理解;要么用 itertools.chain.from_iterable。现在,可以写成 [*doc.chunks for doc in results]。把模型流式返回的一组局部 JSON 对象合并起来,也可以使用 {**part for part in parts}。好读,朴素,够用。
UTF-8成为默认值,有些脚本会有感觉
PEP 686 在2022年就已经被接受,并把目标定在3.15。现在,这个时间到了。Python 默认启用 UTF-8 模式,不再像以前那样仅根据系统 locale 决定这些文本编码行为。没有显式指定编码的 open("file.txt"),默认也会使用 UTF-8。
对大多数 Linux 和 macOS 用户来说,变化不大,因为原本的 locale 就已经是 UTF-8。
更容易受到影响的是 Windows 用户。过去,默认编码可能跟随传统代码页。某些脚本一直默默用该编码写文件、再读回来,升级后却改用 UTF-8 读取;只要数据超出 ASCII 范围,就可能出现错误解码或异常。
总体来看,这对 AI 工作是好事。这个领域到处都是 Unicode:多语言数据集、带着弯引号和 emoji 的模型输出,以及印地语、日语等各种语言的提示词。
那种“只有某个同事的 Windows 电脑才报 UnicodeDecodeError”的问题,应该会少一些。需要旧行为时,可以显式传入 encoding="locale",或者使用 PYTHONUTF8=0、-X utf8=0。
结构化输出时代,类型系统也在补位
有两个 typing 相关 PEP,正好对应现在大家校验 LLM 返回结果的方式。
PEP 747 引入 TypeForm,用于标注接收“类型表达式本身”的参数,而不是接收该类型的普通值。听起来有点抽象,但想想结构化输出工具的函数签名,就容易理解了。
过去,某个函数接受 list[Invoice] | None 这样的类型表达式作为参数时,很难完整地向类型检查器描述:这个参数与返回值类型之间,到底是什么关系。
验证库和结构化生成工具的作者,会直接受益。使用这些库的人,也能得到更准确的类型提示和自动补全。
PEP 728 则允许 TypedDict 声明额外键的值类型,或者把结构关闭,不允许出现额外键。模型输出经常夹带没人要求的字段,因此,能够在类型里表达“这些键必须有,其他键的值必须是字符串”,很实用。PEP 800 关于不相交基类的变化更小众,主要帮助类型检查器理解哪些类不能组合。需要注意,这些类型声明本身并不等同于运行时数据校验。
JIT和自由线程:确实进步了,但别想太夸张
实验性的 JIT 迎来了较明显的一次升级。按 rc3 发布说明,它在 x86-64 Linux 上,相对标准解释器取得约7%—8%的几何平均性能提升;在 AArch64 macOS 上,相对尾调用解释器提升约11%—12%。Windows 64位二进制版本也开始使用尾调用解释器。
这些是扎实的工程成果。但它们未必能明显改变模型推理延迟,因为推理通常不是在 Python 解释器里执行,而是在 CUDA 内核、Rust tokenizer 中,或者通过 HTTP 调用另一台机器上的 GPU。
JIT 更可能帮助那些连接各个环节的代码:解析、路由、重试、后处理。值得拥有,但单凭这一项,还不至于让所有项目立刻升级。
自由线程也在持续推进。官方 macOS 安装程序默认包含相关支持,但这不等于所有程序都默认以无 GIL 模式运行。
从长期看,更重要的是 PEP 803、820 和793 引入了面向自由线程构建的稳定 ABI 目标:abi3t。它让 C 扩展作者有机会发布一个跨多个无 GIL 解释器版本使用的 wheel,而不必每个版本都重新构建。
这些底层配套不怎么吸引眼球,却是 NumPy、tokenizer 绑定和其他 AI 依赖,逐渐把自由线程当成常规选项之前,必须补齐的基础。
还有一个应该坦诚说明的变化:此前引入的增量垃圾回收器,因为内存问题,被回退为分代 GC。不是每一次性能尝试都会成功。发现问题后及时退回去,总比带着回归问题发布更好。
那些小变化,可能真能省下一个下午
asyncio TaskGroup.cancel()
现在,可以提前取消整个 TaskGroup。如果你把同一个问题同时发给三家模型服务,只想保留第一个满意的答案;或者让多个工具调用竞速,拿到结果后停止其他任务,这个功能可以替代不少手写取消逻辑。
错误提示,开始认识其他语言的习惯
Python 3.15 会在你误用其他语言的方法名时,提示对应的 Python 写法。比如 [1, 2, 3].push(4),会建议使用 .append;"hello".toUpperCase(),会建议使用 .upper。
对元组调用 .append,可能提示你想用的是列表;属性不存在时,也可能建议类似 .inner.area 的嵌套路径。官方的出发点,是帮助从其他语言转来的开发者。
但实际情况是,现在很多混着 JavaScript 习惯的 Python 代码,是模型写出来的。更具体的错误提示,可以让这类问题一眼就暴露。
启动文件,更容易审查了
PEP 829 引入 .start 文件,以 pkg.mod:callable 的形式指定启动调用目标,同时弃用过去在 .pth 文件中放入 import 行的做法。
仅仅因为装了某个包,就让代码在每次解释器启动时执行,一直是 Python 打包机制里比较难审查的角落,也容易藏进不该有的行为。
把它变得更明确、更便于审计,是供应链层面的改进。尤其现在,AI 开发者安装新包的速度,比以前更快了。
还有一些零碎,但实用的变化
包括新的 math.integer 模块(PEP 791)、更清楚地表达 re.match() 行为的 re.prefixmatch()、用于减少复制的 bytearray.take_bytes()、base64 中新增的 Base32、Ascii85、Base85 和 Z85 辅助函数、json 的 array_hook 参数,以及 REPL 中带颜色、能区分对象类别的补全。
与此同时,ast、typing、pathlib、importlib 等模块里,也移除了不少已弃用内容。因此升级之前,别忘了打开警告,跑一遍测试。
你的AI助手,可能还不知道这些
下面这件事,是那些“这次改变了一切”的文章经常略过的。
越来越多新 Python 代码,是编程模型写的,或者至少由它们先起草。模型从多年的公开代码中学习,而那些代码里,几乎没有 lazy import、frozendict、sentinel、TypeForm、TaskGroup.cancel(),也没有 [*x for x in y]。
让助手优化一个启动缓慢的 CLI,它可能还是会把导入搬进函数。让它写一个可哈希的配置对象,它可能会想到 MappingProxyType,或者找第三方包;但前者并不能直接解决普通映射不可哈希的问题。
它还可能继续写 if TYPE_CHECKING:,在每个 open 调用里加上 encoding="utf-8"。因为在它熟悉的代码中,“好的 Python”一直长这样。
恰好适合 AI 时代的新功能,反而可能是你的 AI 最不会主动使用的功能。
这不是在责怪模型。语言新特性一直传播得很慢,需要靠文章、技术分享和代码审查逐步普及。现在不同的是,相当一部分代码“作者”,依赖某个时间点的训练数据,也不会自动读懂最新发布说明。
因此,3.15 写法的普及,不会只跟着发布日期走。它还取决于两件事:模型多久能学到发布后的代码,以及有多少团队愿意明确告诉工具,项目到底面向哪个 Python 版本。
所以,最早受益的,未必是安装了最新解释器的人,而是把要求写清楚的人。如果团队使用 Claude Code、Cursor、Copilot,或者任何支持项目指令文件的智能体,可以补上几行:
# AGENTS.md / CLAUDE.md
- 项目目标版本为 Python 3.15+。
- 对 torch、transformers、pandas 等重量级模块,
在模块顶部按需使用 `lazy import`。
- 不可变映射和缓存键,优先考虑内置 `frozendict`;
用作缓存键时,确保其中的键和值可哈希。
- 缺失值标记,优先使用内置 `sentinel()`,而不是 `object()`。
- 默认 UTF-8 已能满足需求时,
不必机械地给每个 open() 添加 encoding="utf-8"。
写这份文件,可能只花五分钟。但它能决定,你的代码库是在真正使用3.15的新能力,还是仅仅能在3.15上运行。
一份简短的升级清单
搜索 Windows 相关代码路径里,没有指定编码的 open() 调用,判断是否依赖旧的 locale 编码。
在3.15.0rc3上运行测试,并使用 -W error::DeprecationWarning,让弃用警告尽早暴露;已移除 API 也要通过实际测试检查。
确认最重要的依赖已经发布适配3.15的 wheel。按发布阶段的兼容约定,针对发布候选版本构建的 wheel 可用于最终版。
把3.15加入 CI 测试矩阵。正式版发布之前,GitHub Actions 的 setup-python 可以通过 allow-prereleases: true 使用预发布版本。
修改代码之前,先用 python -m profiling.sampling attach 分析一个慢服务。火焰图里看到的东西,可能与你猜的完全不同。
在启动最慢的 CLI 或 MCP 服务器上尝试惰性导入,并用 python -X importtime 测量。
更新智能体的项目指令,让工具按3.15的能力写代码,而不是继续沿用3.11时代的习惯。
Python 3.15 不是一场革命。谁要是拍着胸口说它改变了一切,我反而会多留个心眼。它更像是把开发者多年来一直绕着处理的事情——慢导入、不可哈希字典、临时拼出来的缺失值标记、编码差异,以及难以看清的性能问题——纳入更正式的语言和工具支持。
对于 AI 工作负载,进程往往短命,代码大量异步,数据又充满各种 Unicode。这些改进,恰好落在最容易让人难受的地方。
以上就是“Python 3.15 即将到来:这些新特性或将改变 AI 时代的 Python 编程方式!”的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。
扫码二维码 获取免费视频学习资料

- 本文固定链接: http://phpxs.com/post/14594/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料