社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python 3.15 即将到来:这些新特性或将改变 AI 时代的 Python 编程方式

脚本之家 • 3 周前 • 152 次点击  
将 脚本之家 设为“星标⭐”

第一时间收到文章更新

图片

来源 | 大迁世界 (ID:qq449245885)

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。这些改进,恰好落在最容易让人难受的地方。

图片
  推荐阅读:
  1. 为什么从 PDF 里复制出来的字,总是断成一截一截的
  2. 公司宣布我升主管,请全部门喝了奶茶。发工资那天我发现,工资一分没涨,多了三百块岗位津贴。原来所谓的升职,就是换个名头让你多担责
  3. 互联网最古老的恐惧,被 AI 复活了
  4. 裁员为什么先裁技术人员?网友一针见血
  5. 世界上最奇葩的编程语言,悄悄潜入了每一台Windows电脑!

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/202079