
Python 3.15 在 10 月 9 日正式发布,服役五年的 Python 3.10 也在月初正式「退休」。这两件事跟你有什么关系?说白了就是:以后在 Windows 上读中文文件不容易乱码了,自己写的小工具能秒开了,一些以前要额外装库才能做的事,现在 Python 自己就有了。这篇尽量不讲术语,重点不是「改了什么」,而是「改完你能拿它干什么」。
如果你只有三分钟
- 启动快了:模块可以「用到再加载」,命令行工具、脚本能明显快一截。
- 中文不乱码了:读写文件默认用 UTF-8,不用再到处补
encoding="utf-8";但用 GBK 存的老文件要留意。
- 老版本该动了:Python 3.10 已经停止维护,不再有安全补丁,新库也会陆续不支持。
- 装包要小心:今年 Python 的包仓库(PyPI)出了好几起恶意投毒事件,后面有三条能马上用的自保规则。
- AI 相关:做智能体(Agent)现在基本是「用哪家大模型,就用哪家的官方工具包」。
一、两条最实在的变化:启动变快、中文不乱码
1. 启动变快:不再「一开门就把整个仓库搬进屋」
以前写 import 是这样的:只要你写了一句 import pandas,哪怕整个程序只用到一个函数,Python 也会在启动的一瞬间把 pandas 和它依赖的所有东西全都加载一遍。就像你只想拿一支笔,却先把整个仓库的货都搬到客厅。
3.15 加了一个新写法:在前面加个 lazy,意思就是「等真的用到再加载」。
实际能用来做什么
- 自己写的命令行小工具,打开变快。 你那个依赖一堆库、但每次只用一个功能的脚本,可能从「等两秒」变成「眨眼就开」。
- 云函数省钱。 云上的函数按运行时间计费,启动快一点,冷启动的次数和耗时都少一点,账单会好看些。
- 同事用你工具时少等。 内部工具最劝退的就是「双击之后黑窗口停三秒」。
- 老代码不用大改。 不想用
lazy也可以,原来的import行为完全没变。
有个小坑要提前知道:用了
lazy之后,如果这个库装错了或者没装,报错不会在启动时出现,而是在你第一次真正用到它的时候才崩。所以别在关键的启动流程里对「可能会失败」的库用懒加载。
2. 中文不再乱码:默认编码统一成 UTF-8
这个改动对国内开发者来说,价值可能比「启动变快」还大。以前 Python 读写文件用哪种编码,取决于你电脑的「地方习惯」——Windows 中文版常常是 GBK,Linux 一般是 UTF-8,Mac 又是另一套。结果是同一份代码,在你电脑上正常,扔到服务器上就读出一堆乱码,或者直接抛 UnicodeDecodeError。
打个比方:以前是各说各的方言,你听不懂对方;现在规定大家统一说普通话。3.15 之后,open() 默认就是 UTF-8,不管在什么系统上。
实际能用来做什么
- Windows 上读 CSV 不再报错。 以前动不动就要写
open("a.csv", encoding="utf-8"),现在不写也行。
- 代码换台机器就能跑。 本机写好的脚本丢到服务器、丢进 Docker 容器,读文件的行为终于一致了。
- 日志、JSON、配置文件统一口径。 处理中文日志、导出中文报表这类活儿最省心。
- 爬虫存数据少踩坑。 抓下来的中文内容写文件、再读回来,不会莫名其妙变成问号。
我的老项目会不会坏?
如果你的代码本来就依赖「本地编码」,升级后可能出问题。最典型的是两类:一是要读一些历史遗留的 GBK 文件(比如老 ERP 导出的数据),二是代码里用了
locale.getpreferredencoding()来判断编码。升级前先跑一遍这个命令,它会把所有「没写 encoding、依赖默认值」的位置列出来:
建议做法:重要的文件读写,还是老老实实写
encoding="utf-8"。既然要兼容旧版本 Python,写了反而不吃亏。
二、三个「装完就能用」的小东西
这三个不属于大改动,但都挺顺手,属于「以前要绕一下,现在一句话」的类型。
不能改的字典(frozendict)
Python 的字典是能随便改的。你写好一份配置表,传给别的函数,结果一不小心在里面被改掉了,排查半天。现在有了 frozendict,顾名思义,冻住的字典,谁改都报错。
实际能用来做什么:把配置项、常量表、状态码映射这类「本来就不该变」的数据冻起来,能挡掉不少手滑。另外,普通字典不能当字典的键,frozendict 可以,写缓存、做去重的时候方便。
区分「没填」和「填了空」(sentinel)
写函数的时候常遇到一个尴尬:默认值是 None,但调用方真的传了 None 进来,你分不清他是「没传」还是「故意传了空」。以前大家只能自己造一个奇怪的对象来判断,现在 Python 自带了哨兵值。
实际能用来做什么:写配置合并、接口参数默认值、表单字段校验时,「用户没填」和「用户填了空」可以分开处理,不用再靠 None 一个值硬撑。
一行代码摊平嵌套列表
以前要把 [[1,2],[3,4]] 变成 [1,2,3,4],得套两层循环,或者写 [x for L in lists for x in L] 这种看一眼要绕半天的写法。现在可以直接:
实际能用来做什么:处理表格数据、把多个接口返回的列表合并成一个、整理爬虫抓到的分组结果,这类活儿能少写好几行,代码也更好读。
三、程序变慢时,终于能自己查了
「这段代码为什么跑得慢」是很多人卡住的地方。以前的常规办法是:装个第三方工具(比如 py-spy),或者最原始的——到处加 print 和计时。要么麻烦,要么得改代码重启。
3.15 内置了一个叫 Tachyon 的采样分析器。它不用改你的代码,可以直接「挂」在一个正在运行的程序上,隔一会儿抓一次当前在干什么,最后告诉你时间主要花在哪些函数上,还能生成火焰图。
打个比方:以前查病症要抽血化验(改代码、重启),现在像拍个 X 光,人不用动就看清了。
实际能用来做什么
- 线上服务偶尔变慢。 不用停服务,直接挂上去看哪一段在拖时间。
- 数据脚本跑了十分钟。 一眼看出是读文件慢、还是某个循环慢、还是卡在网络请求上。
- 不用再额外装工具。 少一个依赖,少一处环境兼容问题。
但也别指望它直接告诉你「怎么改」:采样分析是统计性质的,它只能告诉你「时间主要花在哪」,具体这行代码为什么慢、该不该优化,还是得你自己判断。有时候最热的那个函数只是受害者,真正的问题在它调用的下一层。
四、Python 3.10 退休了,你的项目要不要动?
10 月 1 日,Python 官方一次性发了五个版本的更新,其中 3.10.22 是 3.10 的最后一版。这意味着 3.10 从今往后不再有任何安全补丁。
版本 | 现状 | 建议 |
3.10 | 已停止维护 | 该升级了,越拖越难 |
3.11 | 只修安全问题 | 老项目最省事的去处 |
3.12 | 只修安全问题 | 手上项目在用就别急着动 |
3.13 | 维护接近尾声 | 可以继续用,但迟早要往前走 |
3.14 | 正常维护 | 稳妥的升级目标 |
3.15 | 最新版 | 新项目可以直接上,老项目先在测试环境跑 |
这件事对你的实际影响,很多人是这么感受到的:某天照着教程 pip install 一个新库,结果报错说「需要 Python 3.11 以上」。再往后,越来越多的库会放弃 3.10,而你被卡在一个装什么都费劲的位置上。
你该怎么判断要不要动
- 先确认自己在用哪个版本:命令行敲
python --version。
- 如果是 3.10 且还在跑线上服务:把它排进计划,至少升到 3.11 或 3.12,改动通常不大。
- 如果是 3.11 / 3.12:不着急,能安心用到 2027、2028 年。
- 如果是自己的电脑、只写写脚本:直接装个 3.15 玩玩,装坏了删掉就行。
五、比版本更该关心的事:装个包也可能中招
这部分和 3.15 没关系,但它是今年 Python 圈子里最值得知道的事。
你可能觉得「我又不是大公司,谁会来攻击我」。但真实情况是:攻击者并不盯着某一个人,他们盯的是大家都会装的那几个包。办法通常是冒充维护者、或者偷偷往一个正常包里塞恶意代码。等你在命令行敲下 pip install 的那一刻,恶意代码就已经在你电脑上跑起来了——不需要你打开它,也不需要你写错什么。
今年六月就出过一次(代号 Hades):有人在两天内往 Python 官方包仓库 PyPI 上传了 60 个恶意安装包,涉及 37 个包名。其中最阴的一招是把代码藏在 .pth 文件里——这东西的特点是,只要你的 Python 一启动,它就会执行,哪怕你只是敲了 python -c "print(1)"。之后它会去下载一个 JavaScript 运行时,跑一段混淆过的程序,把你的密钥、SSH 配置、云账号凭证打包偷走。
同一年里,PyPI 还因为恶意包上传得太猛,紧急暂停了新用户注册和新项目创建大约 24 小时。另有一次更值得警惕:攻击者是通过完全合法的官方发布流程把毒包发出去的。这说明「这个包有官方签名、来源可信」并不能保证它就是干净的。
三条能马上用上的自保规则
- 把版本锁死,别裸装最新版。 在项目里写
requests==2.32.3而不是requests。恶意版本通常在上架后 24 到 72 小时内就会被发现并下架——锁定版本,等于不给自己踩窗口期的机会。
- 只装现成的安装包,不让它现场编译。 有些包会带一个
setup.py,安装时会在你机器上执行代码。加一个参数只用预编译好的包,这条路就堵死了。
- 新版本先等一周再升。 别做第一个尝鲜的人。让别人先踩,一周之后再升,绝大多数投毒事件到那时已经处理完了。
一个常见误解:很多人以为「我用了虚拟环境,所以安全」。虚拟环境只是把包装在各自的文件夹里,它不隔离你的硬盘、不隔离你的网络、也不隔离你的密钥。恶意包一旦跑起来,照样能翻你的主目录。
六、装依赖和查错误,合成一条命令了
如果你被 Python 的「工具太多」折腾过,这部分会有共鸣:装包用 pip,隔离环境用 venv,管理项目用 poetry,查错用 mypy 或 pyright……每种工具一套配置,新人拉下代码往往要折腾半天才能跑起来。
10 月 13 日,做 uv(那个特别快的包管理器)和 Ruff(那个特别快的代码检查工具)的团队,把他们的类型检查工具 ty 和 uv 打通了。现在一条命令就能做完「准备好环境 + 装好依赖 + 检查代码有没有类型错误」:
实际能用来做什么
- 新人拿到代码,一条命令就能跑。 不用再对着 README 一步步配环境。
- 本地和线上检查结果一致。 检查工具的版本跟着项目走,不会出现「你电脑上没问题、CI 上红了」。
- 单文件脚本也能被检查。 在脚本开头声明自己需要哪些依赖,它就会单独为这个脚本准备环境。
需要提醒的是,这套东西现在还是实验性的,ty 本身也还没到 1.0。适合自己先玩玩、或者小团队内部试用,不建议明天就把现有流程全换掉。
七、想写 AI 智能体,现在该选哪个?
这部分和数据有关,但结论可以很简单。先看下载量,能大致反映大家在用谁:
工具包 | 月下载量 | 适合什么情况 |
LangChain | 约 1.7 亿 | 接各种模型、做检索,用得最广,但多用来「调模型」而不是「搭智能体」 |
LangGraph | 约 4,480 万 | 要上生产、怕中途崩掉、需要断点续跑,首选 |
AWS Strands | 约 3,710 万 | 在 AWS 上跑,用 Bedrock |
Claude Agent SDK | 约 2,980 万 | 主要用 Claude,想复用 Claude Code 那套能力 |
OpenAI Agents SDK | 约 1,240 万 | 主要用 GPT 系列 |
Google ADK | 约 1,010 万 | 主要用 Gemini |
Pydantic AI | 约 540 万 | 对输出格式要求严格,要拿到结构化的结果 |
CrewAI | 约 240 万 | 想快速做个「几个角色分工合作」的原型 |
AutoGen | 约 40 万 | 已停止更新,新项目别用了 |
怎么选,三句话
- 只打算用一个模型厂商 直接用那家的官方 SDK,最省事,新功能也最先支持。
- 要跑在真实业务里、怕中途出错 LangGraph。它的价值在于任务跑到一半崩了能从断点接着跑,还能中途停下来等人确认。
- 只是做个 demo 哪个顺手用哪个,不用纠结。
另外有两个容易被忽略的点。一是下载量高不代表适合你:像 AWS Strands 的下载量里有很大一部分是工具链自动装的,不是真有人在用。二是 AutoGen 已经停更了(最后一次更新是今年 4 月),这类框架一旦没人维护,后面迁移的成本全落在你头上。
八、那个「去掉 GIL」的传言,先别急着高兴
GIL 可以理解成一条规矩:厨房里同一时刻只允许一个人做饭。所以 Python 的多线程在干纯计算活儿时,其实是在排队,多核优势用不上。这些年一直有人说「Python 要去掉 GIL 了」。
3.15 确实在这条路上往前走了一步,但对普通开发者来说,有三件事必须先搞清楚:
- 你装的普通 3.15 里,GIL 还在。 想要无 GIL 得专门装另一个版本(通常带个
t),官方在 macOS 的安装包已经默认包含它了,但这是两个不同的东西。
- 无 GIL 不等于更快。 单线程跑的时候,它反而比普通版慢 1% 到 8%,内存还更占。只有「多线程 + 纯计算」这种场景才有明显收益。
- 很多库还没准备好。 10 月 20 日有人抽查了 PyPI 上下载量最高的 500 个包,结果只有 73 个提供了无 GIL 版本,其中只有 14 个公开声明支持。torch、numba、triton 这些重头戏都还没跟上,卡点在 Cython——很多包都靠它来编译。
所以现在该怎么办:大概率什么都不用做。除非你手上正好有「多线程跑纯计算、慢得受不了」的任务,并且用到的库都支持,否则换无 GIL 版本没有任何收益,反而要自己处理加锁的问题——GIL 没了之后,原本靠它兜底的那些「看起来线程安全」的写法就不安全了。
九、给你的行动清单
看到这儿如果只记一件事,就照下面这张单子做。
如果你只是自己写写脚本
- 装个 Python 3.15,随便写两行代码试试,坏了删掉重装就行。
- 把常用的脚本跑一遍
python -X warn_default_encoding 脚本.py,看看有没有编码隐患。
- 试试在启动慢的脚本里给重库加个
lazy,感受下差别。
如果你在维护项目或带团队
- 确认线上跑的是哪个版本,3.10 的排进升级计划。
datetime.utcnow()在 3.15 里已经被删掉了,全局搜一遍换成datetime.now(timezone.utc)。
sqlite3.connect()除路径以外的参数,现在必须用关键字传,比如timeout=30而不是直接传位置。
- 升级先挑一个不重要的服务试,跑通了再推。
不管你是谁,都建议做
- 依赖版本写死,不要裸装最新版。
- 安装时只用预编译包,别让陌生代码在你机器上现场编译。
- 新版本先等一周再升。
