2023 年 10 月发布的 Python 3.12,表面波澜不惊,实则两个改动直击日常:一是 PEP 701 彻底解开了 f-string 的语法枷锁——反斜杠、引号复用、任意嵌套全部放开;二是 PEP 695 给类型系统换上了一套简洁得多的泛型语法——type 语句定义别名,class Box[T] 直接声明类型参数。再配上更友好的错误提示,3.12 是"写起来最顺手"版本的有力竞争者。

1. f-string 增强:引号、反斜杠、嵌套全放开

3.12 之前,f-string 的表达式部分不能出现反斜杠,不能复用外层引号,嵌套更是受限——这些限制都是早期解析器的历史包袱。PEP 701 重写了 f-string 的语法,全部放开,写法终于和直觉一致:

lines = ["a", "b", "c"]
print(f"{'\n'.join(lines)}")  # 反斜杠转义,3.12 前是 SyntaxError

lang = "Python"
print(f"{f"{lang}"}")  # 同引号嵌套 f-string,3.12 前也是 SyntaxError

def fmt(width):
    return f"{3.14159:{width}.2f}"  # 同一引号随意复用

print(fmt(10))  # '      3.14'

以前为了拼多行文本,得先在表达式外面算好再插值;现在表达式里想怎么写就怎么写。更进一步的例子——多行表达式、表达式内注释、复用引号取字典键,一次全用上:

info = {"name": "Python", "year": 1991}
print(f"结果: {
    info["name"].upper()  # 表达式内注释,3.12 起支持
}")  # 输出: 结果: PYTHON

这段代码在 3.11 及以前会直接 SyntaxError,3.12 起全部合法。新语法能力对比见下表:

能力3.11 及以前3.12
表达式内反斜杠不允许允许
表达式内复用外层引号不允许允许
多行表达式不允许允许
表达式内注释不允许允许
任意层级嵌套 f-string受限完全支持

需要提醒的是,放开限制不等于鼓励滥用:表达式里塞太复杂的逻辑仍然影响可读性,建议保持"表达式只做格式化、逻辑放到外面"的习惯;但那些以前为了绕开语法限制而写的中间变量、字符串拼接,现在都可以删掉了。

2. PEP 695:type 语句与新泛型语法

PEP 695 提供两种新语法。第一,type 语句定义类型别名,取代 typing.TypeAlias 的旧写法:

from collections.abc import Callable

type IntList = list[int]
type Callback = Callable[[int], str]

def first(seq: IntList) -> int:
    return seq[0]

第二,classdef 可以直接携带类型参数,TypeVarGeneric 的样板代码大幅减少。对比新旧两种写法:

# 旧写法:TypeVar + Generic 双导入
from typing import TypeVar, Generic

T = TypeVar("T")

class Box(Generic[T]):
    def __init__(self, value: T):
        self.value = value

    def get(self) -> T:
        return self.value


# 新写法:class Box[T],一个方括号搞定
class Box[T]:
    def __init__(self, value: T):
        self.value = value

    def get(self) -> T:
        return self.value


def first_item[T](items: list[T]) -> T:
    return items[0]

新语法的好处不止是少写几行:类型参数的作用域更清晰(只对当前 class/def 生效),且支持约束,类型检查器(mypy、pyright)对它的推断也更准确。对写过泛型代码的人来说,这几乎是"终于等到你"。

更进一步,PEP 695 支持给类型参数设置约束,例如 class Box[T: str | int] 表示 T 只能是字符串或整数,类型检查器会据此拦截错误用法(带默认值的类型参数则是 3.13 的新增能力)。这些改进让泛型的表达力向静态类型语言看齐。

运行时行为也值得一提:新语法生成的类会把类型参数记录在 __type_params__ 属性里,泛型信息不再是"只存在于注解中"的摆设;配合 get_type_hints 等工具,做序列化、校验框架时能拿到真实的类型参数。

3. itertools.batched 与更好的错误提示

把可迭代对象按固定大小分批,是数据处理里的高频操作,以前要手写生成器,现在一行搞定:

from itertools import batched

for batch in batched(range(10), 3):
    print(batch)
# (0, 1, 2)
# (3, 4, 5)
# (6, 7, 8)
# (9,)

实际场景里,batched 很适合"分批处理"类任务:批量写入数据库时分页提交、给外部 API 调用限速、把大文件按行分批读入内存——生成器是惰性的,不会一次性把所有批次都算出来。

另外,3.12 全面改进了语法错误提示:漏括号、拼错关键字、缩进问题,都会给出带插入符(^)的精确位置和修改建议,调试体验明显提升。对初学者来说,这是最容易感知到的改进。

4. 其它 3.12 更新速览

5. 如何安装 Python 3.12

pyenv install 3.12.9
pyenv local 3.12.9
python3.12 --version
python3.12 -m pip install --upgrade pip
python3.12 -m venv .venv && source .venv/bin/activate

Windows 用户也可以直接到 python.org 下载 3.12 安装包,勾选"Add to PATH"后用 py -3.12 调用;macOS 用户则可用 Homebrew 的 brew install python@3.12。安装器与 pyenv 两种方式选一种即可,不必混用。

6. 值不值得升级?

值得,尤其是写库和写类型标注多的人。f-string 放开限制后,不少为了绕过语法限制而写的"绕路代码"可以直接删掉;PEP 695 让泛型代码的可读性上了一个台阶;3.12 也继承了 3.11 的大部分性能成果,并继续优化了启动速度与内存占用。到 2026 年,第三方库的 3.12 wheel 已非常齐全,升级风险很小。唯一提醒:如果你重度依赖某些较老的 C 扩展库(如特定版本的科学计算库),升级前先用 pip index versions 或直接安装试一次,确认 wheel 支持 3.12;纯 Python 项目基本零成本迁移。总体判断:3.12 是当前"体验最好、风险最低"的版本之一,新老项目都值得考虑。

升级前可以借助 pip checkpip index versions 快速排查依赖兼容性;也可以用 pyenv 装一个 3.12 解释器,把现有虚拟环境的 requirements 导出重建一遍,跑完测试再切换默认版本,全程可控。

对学习路径来说,3.12 也承上启下:它把 3.10 的联合类型、3.11 的 Self 与异常组全部继承,又为 3.13 的自由线程时代准备好了更干净的类型系统。把这一版的特性吃透,后续版本的新东西大多只是锦上添花。

💡 学习建议:把项目里手写的分批逻辑换成 itertools.batched;再挑一个带 TypeVar 的泛型类,用 PEP 695 的 class Box[T] 语法重写一遍,对比新旧代码的篇幅,你会立刻明白这次改版的价值。