在 2026年10月9日发布的 Python 3.15.0 中,未指定编码的文件输入输出均使用 UTF-8 处理,不受操作系统语言和区域设置影响。此外,还新增了先声明模块、首次实际使用时才加载的延迟导入功能。依赖操作系统编码设置的文件处理代码需要检查兼容性,而启动时加载大量模块的命令行工具则有机会缩短启动时间。

1. UTF-8 是默认值,而非强制值

这一变更依据 PEP 686,不仅适用于未指定编码的文件输入输出,也适用于标准输入输出和管道。显式指定编码的代码仍按原样运行。

大多数 Unix 环境已使用 UTF-8 设置,因此主要受影响的是 Windows 用户。依赖原有设置的程序可能出现乱码或错误,有时甚至会在没有报错的情况下损坏数据。

2. 找出依赖点并指定编码

检查顺序很简单。先恢复原有行为,通过警告找出未指定编码的位置,再在相关代码中直接写明所需编码,最后使用新默认值重新测试。设置名称如下。

PYTHONUTF8=0  或  -X utf8=0          关闭 UTF-8 模式
PYTHONWARNDEFAULTENCODING=1  或  -X warn_default_encoding   启用 EncodingWarning
encoding="utf-8" / encoding="locale"   显式指定编码

对于必须使用操作系统所设编码的文件,应指定 encoding="locale"。即使开启 UTF-8 模式,该值仍会使用原有设置的编码。

3. 声明放在顶部,加载推迟到首次使用

PEP 810 中的 lazy 是一个 soft keyword,仅在导入语句前具有特殊含义。带有 lazy 的导入只会为名称绑定一个轻量代理对象,在首次使用该名称时才加载实际模块。

过去,为了推迟加载大型模块,常见做法是把导入语句移到函数内部。新方式可以将导入声明集中保留在文件顶部,同时跳过未使用模块的加载。该功能只能用于模块顶层,在函数或异常处理块中使用会产生语法错误。

4. 收益与成本一同转移

需要兼容旧版本的库可以声明模块名称列表,而不使用 lazy。在 3.15 中,只有列表中的模块会被延迟加载;在旧版本中,该声明会被忽略,模块仍像往常一样立即加载。

__lazy_modules__ = ["json", "pathlib"]

import json # lazy
import os # still eager

虽然也有运行选项可以将所有可延迟的顶层导入统一设为延迟导入,但 PEP 810 不建议库开发者使用这种全局控制。未使用的模块越多,延迟导入就越有机会降低启动成本。不过,实际使用模块的加载成本、加载错误,以及插件注册等初始化操作都会推迟到首次使用时。对于依赖导入时副作用的代码,改用显式初始化函数更安全。

默认使用 UTF-8 促使我们重新检查对原有编码的依赖,延迟导入则促使我们重新审视初始化时机。