Python 3.15 发布终结 3.10 时代,并解锁 Python 3.11
Python 于 10 月 9 日发布 Python 3.15,而 Python 3.10 也同步结束其支持生命周期。对于开源维护者来说,这一组合带来了一次不同寻常的双向升级:他们既获得了 3.15 的新特性,也可以开始要求使用五年前引入的能力。
最受瞩目的新增功能是显式延迟导入,它允许应用程序将特定模块的加载推迟到首次使用其名称时。这项改动旨在解决命令行工具、测试套件和拥有庞大依赖树的应用中长期存在的启动延迟问题。
不过,Python 3.15 只是这个故事的一半。支持所有仍处于活跃状态 Python 版本的开发者,现在可以移除对 3.10 的兼容性,并无需回退方案地使用 Python 3.11 特性。对于许多成熟项目而言,提高最低兼容版本所带来的即时收益,可能比直接针对 3.15 开发更大。
因此,核心矛盾并非 Python 3.15 与 Python 3.14 之间的比较,而是在广泛的版本支持与使用更新语言和标准库特性之间进行取舍。每年的新版本都会同时推动这一等式的两端向前发展。
Python 3.15 改变导入、性能分析等多个方面
Python 3.15 为开发者提供了针对启动速度和运行时可观测性的定向工具,而非依赖某一项笼统的性能宣称。
Python 项目在 1,012 名贡献者提交 5,643 次提交后发布了稳定版本。该版本由 Hugo van Kemenade 负责发布管理,在第三个候选版本之后推出,时间较原计划略有延后。
显式延迟导入是最容易辨识的语言改动。PEP 810 引入了一个可置于普通导入语句之前的软关键字:
软关键字只会在特定语法位置具有特殊含义。开发者仍可在其他位置将 lazy 用作普通标识符。
这些语句会在 Python 处理模块时绑定名称,但会将目标模块的加载推迟到代码首次访问该名称时。解析完成后,该绑定的行为与普通即时导入创建的绑定一致。
这一时机至关重要,因为导入一个包往往会触发大量额外导入。命令行应用程序可能在判断用户请求的是哪个子命令之前,就加载数据库客户端、机器学习库或插件依赖项。
新语法让维护者可以保留模块级导入,同时避免一部分不必要的启动工作。他们不再需要将每个高开销导入都隐藏在函数内部,或构建自定义代理层。
Python 3.15 也支持更广泛的控制方式。应用程序可以全局请求延迟行为、禁用它,或提供一个筛选器来决定哪些导入继续保持延迟。默认行为依然保守,因为普通导入语句仍然是即时执行的。
该功能也带有重要限制。lazy 关键字只能在模块作用域使用。Python 会拒绝在函数、类、异常处理块、未来导入和星号导入中使用它。
这些限制体现了这一设计对可预测行为的重视。获接受的显式延迟导入提案更倾向于局部且可见的决策,而不是悄然改变整个进程中的每一项导入。
Python 此前曾通过 PEP 690 考虑过更广泛的方案。该提案追求更自动化的延迟行为,但最终被拒绝。PEP 810 保留了可选的全局控制,同时将显式语法作为常规入口。
此次发布的影响不止于导入。Python 3.15 在推导式中加入了解包功能,使部分嵌套列表、集合和字典操作更为简洁。它还引入了内置的 sentinel 和 frozendict 类型。
哨兵值是用于区分缺失值与 None 等有效值的唯一标记。frozendict 则是一种不可变映射,当其键和值均可哈希时,它本身也可哈希。
根据 PEP 686,Python 现在将 UTF-8 作为默认编码。这减少了对操作系统区域设置的依赖,但处理外部文件的应用仍应在格式要求确定性时明确指定编码。
标准库新增了重新组织的性能分析包,以及高频统计采样性能分析器 Tachyon。采样性能分析器会定期捕获调用栈,以较少干预提供有价值的运行时证据,而不必进行完整追踪。
官方功能摘要还列出了对类型系统、诊断、C API 和自由线程构建的改进。官方 macOS 安装包现在默认安装自由线程支持。
这些变化共同使 Python 3.15 成为一次覆盖广泛的平台发布。不过,它们并不意味着每个应用仅通过更换解释器版本就会变得更快。
这一差异引出了对维护者更具影响力的问题:支持窗口上限的提升,也会带动其下限上移。
Python 3.10 的退出改变最低支持版本
Python 3.10 结束生命周期让维护者能够简化代码,但前提是他们的用户已经完成升级。
Python 3.10 最初于 2021 年 10 月发布。其最后一个安全版本于 2026 年 10 月发布,完成了 Python 标准的五年支持周期。
该版本已不再属于项目的受支持版本。这意味着核心团队不会为仅影响 Python 3.10 的新问题提供常规错误修复或安全更新。
开源项目仍须自行决定支持策略。生命周期结束并不会自动阻止软件包安装或运行;它改变的是继续支持该版本的风险和维护成本计算。
一些维护者遵循完整的上游支持窗口。他们会测试 Python 项目仍支持的每个版本,并在最旧解释器正式退役后停止支持它。
Simon Willison 在多个开源项目中采用这一做法。他的工具包括可扩展的数据发布应用 Datasette,以及带有插件系统的命令行界面 LLM。
停止支持 3.10 后,这些项目便可将 Python 3.11 视为新的最低版本。这一变化消除了为模拟语言中已存在数年的特性所需的工作。
影响首先会体现在持续集成中。项目可以移除 3.10 测试任务、减少版本特定分支,并更新打包元数据。文档和开发环境随后也能统一到更新的基线之上。
依赖项也会更易于管理。维护者不再仅仅因为最新版已停止支持 Python 3.10,就不得不使用旧版软件包。
不过,这一决定仍会给用户带来压力。Linux 发行版、受管企业系统、设备和内部平台并不总是遵循 Python 的年度发布节奏。
一个团队可能运行着仍受维护的操作系统,但其系统 Python 版本低于应用的新最低要求。替换该解释器可能牵涉基础镜像、构建流水线、部署工具和安全审批。
因此,库作者需要使用情况证据,而不仅是上游日历。下载统计、用户报告、部署清单和下游发行版政策,都能揭示 3.10 是否仍具有重要性。
最稳妥的过渡方式是明确说明。维护者应宣布新的最低版本、更新软件包元数据,并在自身支持能力允许时发布最后一个兼容分支。
准确的元数据很重要,因为软件包安装器可在安装前拒绝不兼容的发布版本。缺少这一信号时,用户可能会安装一个无法运行的版本,随后遇到语法或导入错误。
这正是 Python 年度发布间接施加压力的方式。每个新版本都会促使生态系统项目重新考虑自己能合理测试多少旧解释器。
支持五个 Python 版本,会让操作系统、依赖版本、可选功能和原生扩展之间的组合成倍增加。即使是规模适中的兼容性矩阵,也可能消耗大量持续集成时间。
成本并不限于计算资源。贡献者还必须理解兼容性辅助工具、条件导入,以及仅为旧运行时存在的代码路径。
但立即放弃用户,会以采用摩擦为代价换取维护简化。更可行的路径是制定与可衡量上游里程碑挂钩的文档化支持政策。
对于遵循这一政策的项目,Python 3.15 的意义不只是新增特性。它还解锁了一批此前无法在共享库代码中使用的 Python 3.11 改进。
为什么 Python 3.11 成为眼前的升级目标
对许多维护者而言,实际回报并不是编写仅适用于 Python 3.15 的代码,而是终于可以将 Python 3.11 视为普遍可用。
Python 3.11 引入了 tomllib,这是一个用于解析 TOML 配置文件的标准库模块。支持 Python 3.10 的项目通常依赖兼容软件包 tomli,或维护条件导入。
一旦 Python 3.11 成为最低版本,这一兼容层便可以移除。更少的分支意味着更简单的安装要求,也减少了仅用于在等效接口间选择的代码。
异步应用也获得了另一组重要工具。asyncio.TaskGroup 将相关异步任务作为一个整体进行管理,而 asyncio.timeout() 提供了结构化的超时上下文。
任务组支持结构化并发,即子任务始终绑定在明确的父作用域内。与手动收集彼此无关的任务相比,这让取消操作和错误传播更易于理解。
Python 3.11 还引入了异常组和 except* 语法。异常组可将多个失败一并携带,当并发任务可能独立失败时尤其有用。
这些功能与 TaskGroup 自然契合。如果多个子操作失败,调用者可以接收并处理它们的错误,而不会丢失除其中一个任务以外的所有错误信息。
新的最低版本还提供了 datetime.UTC,这是 datetime.timezone.utc 的简洁别名。这项改动虽小,但会在频繁修改的代码中不断累积价值。
SQLite 用户获得了多个接口。Python 3.11 新增了用于读取和修改运行时限制、创建自定义窗口函数,以及以增量方式打开二进制大对象的连接方法。
Datasette 还可以移除一个用于清除 SQLite 授权器的变通方案。这是提高版本下限能够消除维护代码、而不只是启用新语法的具体示例。
类型系统改进也提供了更多升级理由。Python 3.11 包含 typing.Self、必填和可选的单独 TypedDict 字段、可变泛型以及字面量字符串注解。
这些新增能力让库可以更直接地表达既有的运行时契约。它们也减少了对 typing_extensions 的依赖,尽管该软件包在需要支持多代 Python 时仍然有用。
完整的 Python 3.11 变更还包括性能工作和更好的错误消息。无论某个库的最低版本为何,这些收益都已可供运行在 3.11 上的应用获得。
运行时可用性与源代码可用性之间的差异很重要。一个软件包可以在 3.11 下运行得更快,同时因仍需支持 3.10 而避免使用 3.11 语法。
放弃 3.10 改变了维护者能够直接放入已发布源码中的内容。他们现在可以假定 Python 3.11 的解析器、标准库和运行时语义可用。
这一假设简化了贡献者指南。新代码不再需要在审查意见中询问某项功能是否存在于最早受支持的解释器中。
它也改变了自动化工具的目标。格式化工具、代码检查工具、类型检查器和依赖管理器可以面向一个更新的统一基线,并标记意外使用更高版本功能的情况。
这是发布周期带来的延迟红利。一项功能在技术上可用多年后,兼顾兼容性的库才能无条件采用它。
因此,Python 3.15 形成了两条采用路径。应用程序可以立即试验 3.15 功能,而库则可以围绕成熟得多的 3.11 基线进行现代化改造。
这种分化应当影响升级规划。使用 Python 3.11 语言功能所带来的兼容性风险,与立即要求 Python 3.15 并不相同。
前者与上游支持边界一致。后者则会排除所有仍在运行 3.11 至 3.14 的用户,而这些版本均仍处于各自的支持周期内。
显式惰性导入瞄准插件启动难题
Python 3.15 惰性导入最有力的应用场景,来自那些会拖慢每次调用、却只服务于特定工作流的可选依赖。
基于插件的命令行应用清楚地说明了这一问题。用户可能安装一个在插件发现阶段导入大型机器学习框架的插件。
即使用户只是查看帮助、检查版本,或调用无关功能,主命令也要承担这一导入成本。安装一项可选能力,可能让每一条命令都显得更慢。
Willison 在其 LLM 工具中描述了这种行为。使用 sentence_transformers 等重型依赖的插件,即使所请求的操作并不需要它们,也可能为一次调用增加数秒时间。
他的文档此前建议采用手动惰性导入。插件作者可以将导入语句移入实际需要它的函数中,将依赖加载推迟到该代码路径运行时。
这种方法可行,但会让导入语句分散在各个函数中。它可能模糊依赖关系、重复名称解析、增加静态分析难度,并促使插件作者采用不一致的模式。
Python 3.15 的发布为这些作者提供了统一语法。重型依赖仍可在模块顶部附近显式声明,但只会在访问其名称后才被加载。
其收益取决于执行路径。只有在程序结束或产生有用输出前从未触及该名称时,延迟导入才能节省时间。
始终使用重型依赖的插件,只是将成本后移,而非消除。这样的延迟仍可能改善帮助界面或启动反馈,但无法免除必要工作。
惰性导入也可能改变故障出现的时机。使用立即导入时,缺失依赖或导入时异常会在应用启动时出现;使用惰性导入时,故障会在代码首次访问延迟名称时暴露。
如果开发者没有测试相关路径,这种时机会令用户困惑。它还可能使可观测性更加复杂,因为导入失败可能发生在远离模块初始加载阶段的位置。
导入时副作用同样值得关注。一些包会在导入时注册处理器、填充注册表、验证配置,或修改进程状态。
推迟此类包的加载会改变这些效应发生的时间。如果应用的另一部分预期注册已经完成,惰性加载可能引发看似与导入无关的行为。
显式语法之所以有帮助,是因为审查者能够识别每个被延迟的依赖。但它并不能判定延迟该依赖在语义上是否安全。
开发者应在修改导入前进行性能分析。Python 已提供 -X importtime,可报告导入耗时,并帮助定位成本高昂的依赖链。
应用惰性加载后,团队应测量完整的用户操作。如果首次有意义的操作变得不可预测地更慢,仅有更快的初始提示并不足够。
测试还应覆盖不使用可选依赖的执行路径。这些用例能够证明延迟模块是否确实保持未加载,以及启动节省是否能在真实插件发现过程中延续。
新的 Tachyon 采样分析器解决的是另一类性能问题。它帮助识别运行中进程的耗时位置,而导入计时关注的是模块初始化。
结合使用时,这些工具支持以测量为导向的工作流。团队可以区分启动延迟与稳态计算,而不是将二者都笼统称为 Python 性能。
一位维护者的视角说明了为何这一机制比一份功能清单更重要。当一个已安装插件影响每一次命令调用时,其价值便清晰可见。
这使得命令行工具、测试运行器、插件宿主、笔记本环境和大型 Web 应用可能成为早期采用者。它们都可能拥有广泛的依赖图,而许多会话永远不会执行其中部分代码。
不过,库在引入仅限 3.15 的语法时应保持谨慎。即使条件分支能避免执行它,旧解释器也无法解析 lazy 关键字。
PEP 810 为需要在旧版本间保持源码兼容的项目提供了 __lazy_modules__。模块可以标识选定依赖,同时保留看似普通的导入语句。
这一过渡方案很重要,因为生态系统不会立刻将最低版本提升至 3.15。应用程序比可复用库更容易控制自己的运行时,而后者无法同样控制其使用者。
因此,显式惰性导入首先强化了 Python 3.15 对应用程序的价值。库对它的采用将取决于打包政策、工具支持,以及延迟导入是否仍然易于理解的证据。
Python 3.15 并非通用提速升级
开发者应将有针对性的性能功能,与普通 Python 3.15 程序会自动大幅加速的说法区分开来。
该版本包含对 CPython 实验性即时编译器的持续工作。JIT 编译器会在程序运行期间,将频繁执行的代码路径转换为机器码。
Python 的发布页面报告了实验性 JIT 相对于选定解释器配置的几何平均性能提升。其指出,在 x86-64 Linux 上提升为 7% 至 8%。
对于 AArch64 macOS,已发布的比较显示,相较于尾调用解释器,性能提升为 11% 至 12%。这些数字描述的是特定配置,而非所有工作负载。
JIT 仍处于实验阶段。团队不应在未测试自身代码、依赖、硬件和延迟要求的情况下,将基准测试摘要直接转化为生产预期。
Willison 所讨论的独立测试发现,在实验性 JIT 之外,Python 3.15 整体上与 3.14 相近。斐波那契测试显示出有意义的 JIT 增益,但单项基准测试无法代表典型应用。
这一细节并未削弱此次发布的价值,而是明确了价值所在。
惰性导入可以改善加载不必要模块的命令。采样分析器可以揭示运行中服务的瓶颈。但两者都不意味着计算密集型循环会自动变快。
同样,自由线程 Python 通过让兼容构建中的全局解释器锁变为可选,瞄准并发限制。它提供了另一条采用路径,而非为未改动应用带来免费的加速。
原生扩展必须支持相关的自由线程接口。应用行为还取决于同步机制、内存分配、扩展兼容性以及实际可并行执行的工作。
Python 3.15 强化了自由线程构建的稳定 ABI。稳定的应用程序二进制接口可以减少扩展作者为新的解释器版本重新构建二进制文件的频率。
这一进展有助于生态系统成熟,但打包覆盖率仍是核心。团队在更改生产构建之前,应确认完整依赖栈是否发布了兼容的 wheel。
各平台的默认设置也不同。官方 macOS 安装程序默认包含自由线程支持,而 Windows 二进制发行版使用尾调用解释器。
这些实现选择对发行方和性能工程师很重要。大多数应用团队应先识别已测量的问题,再选择相应的解释器配置。
同样的审慎态度也适用于采用惰性导入。延迟加载会影响时机、错误位置和初始化顺序,因此基准测试必须包含正确性检查。
若启动改进引入间歇性注册错误,就不是性能收益。若中位延迟降低,却让首次真实操作出现令人意外的延迟,也同样不是。
团队应记录冷启动、热启动、峰值内存、首次操作延迟和稳态吞吐量。这些指标能够揭示延迟工作实际被移往何处。
维护者还需要针对打包工具、静态分析器、调试器和代码格式化工具进行兼容性测试。新语法可能暴露解释器之外的假设。
Python 3.15 的显式设计应使工具支持易于管理。然而,不能仅因 CPython 接受该语法,就假定生态系统会立即全面支持。
这也是 Python 3.11 基线重要的另一原因。成熟的 3.11 功能已有多年的部署经验和广泛的工具支持。
因此,一个合理的项目可以围绕 3.11 对其通用代码进行现代化改造,同时将 Python 3.15 实验隔离在应用特定分支之后。这些决策解决的是不同问题。
在多个仓库中维护技术决策的团队,也需要对基准测试、兼容性发现和推广例外进行可搜索的记录。共享的工程知识库可以保留每次版本边界变更的原因。
此次发布应推动测试,而非笼统断言。Python 3.15 提供了更精确的工具,但结果仍取决于开发者如何使用它们。
Python 3.15 发布后值得关注的事项
接下来的证据将来自惰性导入的采用、软件包支持边界,以及真实世界中的自由线程兼容性。
第一个信号,是 PEP 810 在知名命令行和插件型应用中的采用情况。这些项目拥有可测量的启动路径和可选依赖,是测试显式惰性导入的天然场景。
有价值的报告将比较采用前后的冷启动、首次命令延迟和内存使用情况。它们还应披露工作是被消除了,还是只是被移至后续交互。
错误报告与积极的基准测试同样重要。涉及导入时注册、循环导入、内省或延迟异常的问题,将揭示该功能的实际边界。
如果成熟应用报告了可重复的收益,且无需复杂的变通方案,这一机制的价值将更具说服力。如果采用需要大量筛选和特殊处理,其初始适用范围将显得更窄。
第二个信号,是维护中的软件包放弃 Python 3.10 的速度。打包元数据和发布说明将显示,上游停止支持是否转化为新的生态系统基线。
渐进式过渡将表明,企业与发行版限制仍超过维护成本节省。更快的过渡则能让库更一致地采用 Python 3.11 功能。
关键区别在于停止对新功能的支持,与破坏现有安装之间的差别。负责任的项目可以停止测试 3.10,同时保留一条兼容旧版本的发布线。
用户在升级前应留意依赖锁定文件。一个基础库提高最低版本要求,可能决定整个应用何时必须迁移。
第三个信号是面向无 GIL Python 的二进制包可用性。当关键数据库驱动、数值计算库或可观测性扩展缺少兼容构建时,解释器支持本身的价值有限。
更广泛的 wheel 覆盖将进一步证明无 GIL部署正从实验阶段走向成熟。持续存在的缺口则会使采用者仍主要集中在能够控制完整原生技术栈的团队中。
开发者还应在具有代表性的服务、数据任务和命令行工作负载中比较实验性 JIT。汇总的基准测试百分比无法替代应用层面的证据。
对维护者而言,眼下的行动更简单:审查项目的最低版本策略,衡量仍在使用 Python 3.10 的用户比例,并识别如今可以移除的兼容性代码。
随后,在持续集成中测试 Python 3.15,但不要将其设为最低版本。这样既能及早发现故障,也能继续让使用受支持旧版本的用户正常使用。
启动较慢的应用应分析导入过程,并找出在常用命令中始终未被使用的依赖。只有这些候选项值得优先尝试惰性导入。
同时记录性能结果,以及任何故障发生时机的变化。未来的贡献者需要了解某项导入为何采用惰性方式,以及已审查过哪些副作用。
Python 3.15 的发布意义重大,因为它同时推动了两只时钟。一只带来新的解释器能力,另一只则让维护者摆脱 Python 3.10 的兼容性负担。
哪一只时钟对你的项目更重要,取决于其用户群。现在就衡量这条边界,再决定下一项改动应面向 Python 3.15 功能,还是更简洁的 Python 3.11 基线。



