
我之前一直使用 Python 3.10 版本来写Python代码。但最近在运行一些新项目时,发现会报错,排查后发现是 f-string 的写法问题——比如用了与外层相同的引号,或者包含反斜杠。查阅资料后才明白,这些用法是 Python 3.12 才引入的新特性。要解决这个问题,要么修改源码,要么就得升级 Python 版本。
趁这个机会,我顺便把 Python 3.8 到 3.14 的更新日志都过了一遍,以便确定今后该把哪个版本作为主力开发版本。

目前来看,Python v3.11 和 v3.12 应该是当下项目的主流选择。考虑到 v3.12 对 f-string 的语法做了大幅放宽,我打算以后主力使用 v3.12,以免再次遇到某些项目因采用放宽后的 f-string 写法而无法运行的情况。
至于 3.13 和 3.14,版本太新,部分第三方库可能尚未兼容,暂时先不考虑。
除了根据外部项目需求来选择 Python 版本之外,如果你还需要用 Python 调用 MATLAB 代码,那就还得考虑 MATLAB 版本与 Python 的兼容性——在 MATLAB 2024a 及更早版本中,只能与 Python 3.11及更早版本相互调用。因此,我现在也把 MATLAB 升级到了 R2024b。

附:Python 3.8–3.14 的主要更新摘要如下
https://docs.python.org/zh-cn/3/whatsnew/3.8.html
这是 Python 3.8 最具争议的语法新增。它允许在表达式内部进行赋值,将"赋值"和"判断"合二为一。
在 3.8 之前,如果你想在 while 循环中读取数据并检查长度,必须写成:
n = len(data)
while n > 0:
print(n)
n = len(data)
海象运算符让这变成一行:
while (n := len(data)) > 0:
print(n)
另一个典型场景是在列表推导式中复用计算结果。比如正则匹配时,无需重复调用 match():
import re
# 3.8 之前:需要单独写循环或重复调用
# 3.8 之后:在推导式内直接赋值并复用
results = [m.group(0) for line in lines if (m := re.search(r'\d+', line))]
这个特性由 Guido van Rossum 亲自推动,但在社区引发了激烈讨论,甚至一度导致他辞去 BDFL(终身仁慈独裁者)职位。
海象运算符 (:=) 虽然方便,但并不是所有地方都适合用,过度使用它,会使代码变得更不直观,与Python之禅(明了优于隐晦;简单优于复杂)想违背,这也是当年提案的争论之处。
比如下面这个用法就会代码变得难维护
result = (z := (y := (x := compute())) + y) + z
函数参数列表中新增 / 符号,将其左侧的参数标记为"只能按位置传递,不能按关键字传递"。
defpow(x, y, /, mod=None):
return (x ** y) % mod if mod else x ** y
pow(2, 3) # ✅ 正确
pow(2, 3, mod=5) # ✅ mod 在 / 右侧,可以按关键字传
pow(x=2, y=3) # ❌ TypeError: x 和 y 在 / 左侧,不能按关键字传
这解决了 C 扩展函数(如 len()、str.find())长期以来与纯 Python 函数在调用约定上的不一致问题。它也让 API 设计者能明确区分"实现细节参数"和"公开接口参数"——内部参数放在 / 左侧,用户无需关心其名称。
在 f-string 的花括号内加 =,Python 会自动输出变量名=数值结果。
x = 42
print(f'{x=}')
# 输出: x=42
print(f'{x * 2 = }')
# 输出: x * 2 = 84
之前版本需要写 f'x={x}',现在只需 f'{x=}'。
这是从 importlib_metadata 第三方包正式纳入标准库的模块,用于读取已安装包的元数据。
from importlib.metadata import version, requires, entry_points
version('requests') # 返回 '2.31.0'
requires('django') # 返回依赖列表
entry_points()['console_scripts'] # 读取包的入口点
它替代了 pkg_resources 中大量笨重的 API,让检查环境、构建插件系统变得更直接。
✦ Vectorcall 协议(PEP 590)
这是 C API 层面的底层优化,普通用户无感知,但影响深远。它定义了一种更快速的函数调用协议,减少了参数打包/解包的开销。Python 3.8 的内置类型(如 dict()、list())开始采用这一协议,为 3.11 的全面性能爆发奠定了底层基础。
✦ math.comb() 和 math.perm():组合数和排列数计算
✦ math.isqrt():整数平方根,比 int(math.sqrt(n)) 更精确高效
✦ statistics.fmean() 和 statistics.geometric_mean():更快的浮点均值和几何平均数
✦ typing.Final、typing.Literal、typing.Protocol:类型系统的关键补充
✦ multiprocessing.shared_memory:跨进程共享内存,无需序列化开销
https://docs.python.org/zh-cn/3/whatsnew/3.9.html
终于可以用运算符合并字典了:
defaults = {'a': 1, 'b': 2}
overrides = {'b': 3, 'c': 4}
# 合并产生新字典,右侧优先级高
merged = defaults | overrides
# {'a': 1, 'b': 3, 'c': 4}
# 原地更新
defaults |= overrides
# defaults 变为 {'a': 1, 'b': 3, 'c': 4}
之前只能用 {**d1, **d2} 或 d1.copy(); d1.update(d2),前者在键冲突时的行为不够明确,后者是语句而非表达式。
从 3.9 开始,你可以直接用 list[int]、dict[str, float] 作为类型提示,无需再从 typing 模块导入大写版本。
v3.9之前
from typing importList, Dict, Set, Tuple
defprocess(data: List[int]) -> Dict[str, int]:
...
v3.9之后
# 3.9 之后
defprocess(data: list[int]) -> dict[str, int]:
...
这不仅是少写几行导入的问题,它标志着 Python 类型系统正在从"外部附加"走向"语言原生"。list、dict 等内置类型在运行时就是自身,不再需要 typing.List 这种代理对象。不过 PEP 585 也引入了"软废弃"机制,typing.List 等不会立即消失,但社区逐渐转向小写风格。
Python 从 1990 年代起一直使用自研的 LL(1) 解析器,它要求语法规则能通过一个字符的向前看(Lookahead)无歧义解析。这个限制越来越成为语法创新的瓶颈。
3.9 引入了基于 PEG(Parsing Expression Grammar)的新解析器,由 Guido 亲自设计实现。PEG 解析器更灵活、更强大,允许更复杂的语法规则。虽然 3.9 的语法本身没有大变化,但解析器替换为 3.10 的 match/case 结构模式匹配铺平了道路——这种复杂的语法在旧解析器下几乎不可能实现。
内置 IANA 时区数据库支持,终于无需依赖 pytz 了。
from zoneinfo import ZoneInfo
from datetime import datetime
dt = datetime(2024, 7, 13, 15, 0, tzinfo=ZoneInfo("Asia/Shanghai"))
print(dt.tzname()) # 'CST'
url = "https://example.com"
url.removeprefix("https://") # 'example.com'
filename = "document.txt"
filename.removesuffix(".txt") # 'document'
这解决了长期以来社区用 str.startswith() 配合切片或 str.lstrip()(后者行为错误,因为 lstrip 是按字符集移除而非按前缀字符串)的痛点。
✦ graphlib 模块:内置拓扑排序算法 TopologicalSorter,用于任务依赖排序
✦ ast.unparse():将 AST 节点反序列化为源代码字符串
✦ asyncio 支持多线程事件循环和 asyncio.to_thread() 的雏形
✦ typing.Annotated:为类型提示附加元数据,被 FastAPI 等框架广泛采用
https://docs.python.org/zh-cn/3/whatsnew/3.10.html
这是 Python 3.10 最重磅的语法特性,在3.10 版本之前,Python 从来没有实现switch 语句在其他编程语言中所做的功能。match/case类似于传统语言中的 switch,但功能更强大。它不仅能匹配字面量,还能解构序列、字典和对象,大幅减少繁琐的 if-elif-else 链。
模式匹配支持:
✦ 序列模式:[a, b, *rest] 匹配列表/元组
✦ 映射模式:{"key": value} 匹配字典
✦ 类模式:Point(x, y) 匹配数据类实例
✦ 守卫子句:case [x] if x > 0: 添加条件判断
✦ 通配符:_ 匹配任意值但不绑定
例子
基本模式匹配
x = 10
match x:
case10:
print("x is 10")
case20:
print("x is 20")
case _:
print("x is something else")
在这里,_是一个特殊的“占位符”模式,用于匹配任何值(类似于 else)。
序列模式匹配
defhandle_command(command):
match command:
case ["quit"]:
print("Goodbye!")
case ["load", filename]:
print(f"Loading {filename}")
case ["save", filename, *options]:
print(f"Saving {filename} with {options}")
case {"type": "click", "x": x, "y": y}:
print(f"Clicked at ({x}, {y})")
case _:
print("Unknown command")
handle_command(["load", "data.txt"]) # Loading data.txt
handle_command({"type": "click", "x": 10, "y": 20}) # Clicked at (10, 20)
对象模式匹配
classPoint:
def__init__(self, x, y):
self.x = x
self.y = y
p = Point(0, 3)
match p:
case Point(x=0, y=y):
print(f"Point is on the Y axis at {y}")
case Point(x=x, y=0):
print(f"Point is on the X axis at {x}")
case Point(x, y):
print(f"Point is at ({x}, {y})")
case _:
print("Not a point")
类型提示中可以用 | 替代 typing.Union,更加简洁:
# 3.10 之前
from typing importUnion
deffunc(value: Union[int, str]) -> None:
...
# 3.10 之后
deffunc(value: int | str) -> None:
...
在 3.10 之前,traceback 中的行号有时指向函数定义行而非实际执行行,调试时令人困惑。3.10 通过在字节码中存储更细粒度的位置信息,让异常 traceback 能精确指向出错的具体表达式,而非整行。
3.10 的错误消息开始变得"有温度"了。它不再只是冷冰冰地抛出 SyntaxError,而是尝试理解你的意图并给出建议:
# 你写了:
if x > 0
print(x)
# 3.10 提示:
# SyntaxError: expected ':'
# if x > 0
# ^
对于名称错误,它甚至会提示你可能想导入的模块:
# 你写了:
json.dumps(data)
# 3.10 提示:
# NameError: name 'json' is not defined. Did you forget to import 'json'?
✦ ParamSpec(PEP 612):让装饰器能正确保留被装饰函数的参数类型签名
✦ TypeAlias(PEP 613):显式标记类型别名,如 Vector = list[float] 可写成 Vector: TypeAlias = list[float]
✦ zip(strict=True):严格模式,要求可迭代对象长度一致,否则抛出异常
https://docs.python.org/zh-cn/3/whatsnew/3.11.html
3.11 是 Python 的性能爆发年。通过多项底层优化,官方 pyperformance 基准测试显示比 3.10 平均快 10–25%,某些场景(如纯函数调用、属性访问)快达 60%。
关键优化包括:
✦ 自适应解释器:根据运行时类型特化字节码,热点代码走快速路径
✦ 零成本异常:try/except 没有异常时不产生运行时开销
✦ 内联函数调用:减少 Python 函数调用的 C 栈帧开销
✦ 更快的对象属性访问:优化了 __dict__ 查找路径
这是 CPython 团队" faster-cpython "项目的首个重大成果,让 Python 终于摆脱了"慢语言"的刻板印象。
asyncio.gather() 或并发场景下,多个任务可能同时失败,抛出多个异常。3.11 之前你只能捕获第一个异常,其余的丢失了。
3.11 引入了 ExceptionGroup 和 BaseExceptionGroup,以及 except* 语法:
deffailing():
raise ExceptionGroup("multiple errors", [
ValueError("invalid value"),
TypeError("wrong type"),
KeyError("missing key")
])
try:
failing()
except* ValueError as eg:
print(f"Caught ValueErrors: {eg.exceptions}")
except* TypeError as eg:
print(f"Caught TypeErrors: {eg.exceptions}")
except* 可以匹配异常组中的部分异常,让其余异常继续传播。这为构建健壮的并发错误处理框架提供了原生支持。
TOML 已成为 pyproject.toml(PEP 518/621)的事实标准配置格式,但 Python 长期缺乏内置的 TOML 解析器。3.11 终于将 tomli 项目纳入标准库,命名为 tomllib(只读):
import tomllib
withopen("pyproject.toml", "rb") as f:
config = tomllib.load(f)
注意它要求以二进制模式(rb)打开文件,因为 TOML 规范要求按 UTF-8 解析,二进制模式避免了编码问题。
终端中默认启用彩色 traceback,关键行、错误类型、文件路径以不同颜色高亮显示。这不再是第三方库(如 rich、IPython)的专属功能,而是 CPython 原生支持。
✦ asyncio.TaskGroup:结构化并发,确保所有子任务完成后才退出上下文
✦ typing.Self:在类方法中返回自身类型,替代复杂的 TypeVar 技巧
✦ typing.Never:表示"永远不会返回"的类型,替代 NoReturn
✦ math.cbrt():立方根计算
✦ enum.StrEnum / enum.IntEnum 改进
https://docs.python.org/zh-cn/3/whatsnew/3.12.html
这是 f-string 自 3.6 引入以来最彻底的解放。3.11 之前,f-string 的花括号内表达式有诸多限制:不能用反斜杠、不能换行、不能用与外层相同的引号、不能写注释。3.11 全部解除。
# ✅ 反斜杠
f"路径: {path.replace('\\', '/')}"
# ✅ 多行表达式
f"结果: {
x + y
}"
# ✅ 相同引号嵌套
f"{'hello'}"
# ✅ 表达式内注释
f"值: {
x + y # 累加
}"
3.12 之前定义泛型函数需要繁琐的 TypeVar 声明:
from typing import TypeVar
T = TypeVar("T")
deffunc(x: T) -> T:
return x
3.12 引入了直接在函数/类签名中声明类型参数的语法:
deffunc[T](x: T) -> T:
return x
classContainer[T]:
def__init__(self, value: T) -> None:
self.value = value
类型参数 T 的作用域自动限定在函数或类内部,无需全局声明。这不仅减少了样板代码,也让类型提示更贴近其他现代语言(如 TypeScript、Rust)的泛型风格。配合 3.13 的类型参数默认值(PEP 696),泛型系统变得愈发成熟。
这是 Python 并发架构的底层革命。传统上,CPython 的 GIL(全局解释器锁)是进程级别的,一个 Python 进程内所有线程共享同一个 GIL。3.12 开始,每个子解释器(Subinterpreter)可以拥有独立的 GIL。
这意味着你可以在一个进程内运行多个 Python 解释器实例,每个实例独立持有 GIL,真正实现多核并行。虽然 3.12 的 API 还比较底层(主要通过 _xxsubinterpreters 模块暴露),但它为 3.13 的"自由线程"(nogil)和未来的多核 Python 应用铺平了道路。
memoryview 和缓冲区协议长期以来是 C 扩展的专属领域。3.12 将 __buffer__ 协议暴露给纯 Python,让你能在 Python 层面实现支持缓冲区协议的对象:
classMyBuffer:
def__buffer__(self, flags):
returnmemoryview(b"hello")
buf = MyBuffer()
mv = memoryview(buf)
这让纯 Python 库(如图像处理、二进制协议解析)能更深入地参与零拷贝数据传输。
>>> sys.version_info
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'sys'isnot defined. Did you forget to import'sys'?
✦ pathlib 重大改进:pathlib.Path 支持更多文件操作,如 walk()、copy()、move()
✦ os 模块支持 Linux 性能分析事件 perf_event_open
✦ typing 模块性能优化,导入速度提升
✦ 继续移除 distutils 等废弃模块,推动 setuptools 标准化
https://docs.python.org/zh-cn/3/whatsnew/3.13.html
这是 Python 历史上最重要的并发变革之一。传统 CPython 的 GIL 确保了解释器状态的原子性,但也导致多线程无法真正利用多核 CPU。3.13 引入了无 GIL 构建(--disable-gil),编译后的 Python 解释器允许多线程真正并行执行 Python 字节码。
# 在 nogil 构建下,这段代码能利用多核
import threading
defworker():
total = 0
for i inrange(10_000_000):
total += i
return total
threads = [threading.Thread(target=worker) for _ inrange(4)]
for t in threads:
t.start()
for t in threads:
t.join()
虽然 3.13 中 nogil 还是实验性的,单线程性能有约 30% 的惩罚,但它标志着 Python 社区正式向"移除 GIL"这一数十年难题发起挑战。到 3.14,这一惩罚已降至 5–10%。
3.13 引入了实验性的即时编译器(JIT),将 Python 字节码在运行时编译为机器码。它基于复制加修补(Copy-and-Patch)技术,由 LLVM 团队在学术研究中提出,后被 CPython 团队采纳。
JIT 默认关闭,需要通过 --enable-experimental-jit 编译开启。虽然 3.13 的 JIT 覆盖范围有限,性能提升温和(约 2–9%),但它为后续版本的持续优化奠定了基础。到 3.14,JIT 已能覆盖更多字节码和控制流。
长期以来,locals() 的行为像一个"黑箱"——在函数内调用它返回局部变量的快照,但修改这个字典是否会影响实际局部变量?答案因上下文而异,调试器(如 pdb)的实现也因此充满 hack。
3.13 明确了两种语义:
✦ locals() 返回独立快照:修改它不影响实际局部变量
✦ FrameType.f_locals 返回写穿透代理:修改它会直接反映到运行中的局部变量
这让调试器、分析器和元编程工具能更可靠地检查和修改运行时的局部状态。
3.13 的 REPL(交互式解释器)进行了现代化改造:
✦ 语法高亮:输入的代码自动着色,关键字、字符串、注释一目了然
✦ 多行编辑:支持上下箭头在历史记录中浏览多行代码块
✦ 历史记录持久化:退出后重新进入解释器,历史命令仍然保留
✦ 更好的自动补全:基于 readline 的改进,补全体验更流畅
这些功能之前需要 IPython 或 bpython 才能实现,现在 CPython 原生支持。
泛型参数可以指定默认值了:
from typing import TypeVar
T = TypeVar("T", default=int) # 默认类型为 int
classContainer[T]:
def__init__(self, value: T = None) -> None:
self.value = value
# 不指定类型参数时,T 默认为 int
c = Container() # 等价于 Container[int]()
这减少了类型提示的冗余,特别是在泛型容器和回调函数的类型定义中。
iOS 和 Android 被正式列为 Tier 3 支持平台。这意味着:
✦ CPython 的构建系统正式支持交叉编译到 iOS/Android
✦ 官方测试套件会在这些平台上运行
✦ 核心开发者承诺修复平台相关的回归问题
Python 正式进入移动端开发生态,为 Kivy、BeeWare 等框架提供了更稳固的基础。
3.13 删除了 19 个长期废弃的"死电池"模块,包括:
✦ cgi:已被 WSGI/ASGI 框架完全取代
✦ crypt:密码哈希应使用 hashlib
✦ mailcap:媒体类型处理
✦ msilib、spwd、sunau、telnetlib 等
这缩小了标准库体积,减少了安全维护负担,也推动社区使用更现代的替代方案。
✦ warnings 模块支持 action="default" 的细化控制
✦ dbm 模块支持 SQLite 后端
✦ asyncio 支持 asyncio.Barrier 同步原语
✦ typing 模块支持 ReadOnly 和 Required 类型修饰符(TypedDict)
https://docs.python.org/zh-cn/3/whatsnew/3.14.html
t-strings(template strings)在语法上类似 f-strings,但不立即求值,而是生成一个可复用的模板对象:
name = "Alice"
template = t"Hello, {name}!"
# template 是一个 Template 对象,包含原始字符串和插值表达式
# 此时 name 的值尚未被填充
模板对象可以被多次渲染,也可以被库拦截进行安全处理:
# 安全 SQL 参数化(伪代码)
from some_sql_lib import render_sql
query = t"SELECT * FROM users WHERE id = {user_id}"
render_sql(query) # 库会自动将 user_id 转为参数化查询,防止 SQL 注入
与 f-string 的"立即求值"不同,t-string 的"延迟求值"让它天然适合:
✦ SQL 参数化:防止注入攻击
✦ HTML 模板:自动转义用户输入
✦ 国际化(i18n):提取可翻译字符串,在渲染时根据语言环境替换
✦ 日志延迟格式化:避免在日志级别不足时执行昂贵的字符串格式化
这是 Python 在"安全字符串处理"领域迈出的重要一步,填补了长期以来 f-string 在安全性上的空白。
从 3.7 的 from __future__ import annotations 开始,Python 社区一直在探索如何解决类型注解的前向引用问题。当类 A 的方法参数类型是类 B,而类 B 定义在类 A 之后时,你需要写成字符串:
classA:
defmethod(self) -> "B": # 字符串前向引用
...
classB:
...
3.14 通过 PEP 649 彻底解决了这个问题。类型注解默认不再在类定义时强制求值,而是以延迟计算的形式存储。这意味着:
# 3.14 之后,无需字符串引号
classA:
defmethod(self) -> B: # ✅ 直接写 B,即使 B 在后面定义
...
classB:
...
这消除了字符串前向引用的视觉噪音,也让类型注解的语义更清晰——它们不再是"运行时会被执行的代码",而是"供类型检查器消费的元数据"。
sys.remote_exec(pid, script) 允许你安全地附加到一个正在运行的 Python 进程,执行调试脚本,然后分离,无需重启目标进程。
# 在调试终端中
import sys
sys.remote_exec(12345, """
import gc
print(f"Objects: {len(gc.get_objects())}")
""")
这被称为"零开销"是因为:目标进程无需预先开启调试模式,没有持续的性能损耗。只有当调试器附加时,才会注入代码。这对于生产环境的问题排查(如内存泄漏、死锁分析)是革命性的——你不再需要为了调试而重启服务或开启昂贵的监控。
3.13 的 nogil 实验在 3.14 中大幅成熟:
✦ 单线程性能惩罚从约 30% 降至 5–10%
✦ 内存分配器优化,减少了多线程竞争
✦ 更多 C 扩展兼容无 GIL 模式
这意味着 nogil 从"实验性玩具"逐渐走向"生产可用"的门槛。
3.14 的 JIT 覆盖范围显著扩大:
✦ 支持更多字节码指令的编译
✦ 优化了循环和条件分支的机器码生成
✦ 性能提升从 3.13 的温和水平进一步扩大
JIT 与 nogil 的结合,让 Python 在多线程 CPU 密集型任务中的竞争力持续提升。
在 Clang 19+ 编译器下,3.14 使用尾调用(Tail Call)实现字节码分发(opcode dispatch)。这是一种比传统 switch 语句或跳转表更高效的指令分发方式,基准测试显示快 3–5%。虽然提升幅度不大,但它展示了 CPython 团队在现代编译器特性上的积极探索。
3.14 继续打磨错误消息。对于常见的关键字拼写错误:
# 你写了:
whille x > 0:
print(x)
# 3.14 提示:
# SyntaxError: invalid syntax. Did you mean 'while'?
对于缩进和括号不匹配,提示更加精确,甚至能建议缺失的代码结构。
✦ pathlib 继续扩展:支持更多文件系统操作和路径解析优化
✦ typing 模块支持更复杂的类型运算
✦ asyncio 性能优化,事件循环吞吐量提升
✦ 标准库继续清理遗留 API,为新特性腾出空间
✦ Matlab 和 Python选哪个?对不起,小孩子才做选择!我都要!
✦ 安装Python,我选择miniforge而不是Anaconda
✦ 【博客】如何用python读写钙成像的tiff stack:使用tifffile