提起 3.13(2024 年 10 月发布),大家首先想到的是实验性 free-threading 和 JIT。但对每天写代码的人来说,3.13 有一批低调但高频实用的小特性。本文逐个给出可直接抄走的用法——全部在 CPython 3.13.15 与 3.14 上实际运行验证过。
版本要求:无特别标注的代码需要 Python 3.13+。
1. copy.replace():不依赖 dataclass 的"换头术"
以前要"基于某个对象改几个字段得到新对象",要么类自己实现 dataclasses.replace,要么手写。3.13 把这个能力放进了标准库,任何对象都能用:
import copy
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: int
y: int
p = Point(1, 2)
print(copy.replace(p, y=9)) # Point(x=1, y=9) —— 实测
适用面比 dataclasses.replace 广:dataclasses、NamedTuple 都按同一协议支持;自定义类只要实现 __replace__ 方法也能接入。不可变配置对象"改一个字段"的场景从此一行搞定。
2. typing.TypeIs:会收窄类型的类型守卫
3.10 的 TypeGuard 有个反直觉之处:返回 True 只收窄"为真分支"。3.13 的 TypeIs 把两边都收窄了,语义更接近 isinstance:
from typing import TypeIs
def is_str_list(v: list[object]) -> TypeIs[list[str]]:
return all(isinstance(x, str) for x in v)
xs: list[object] = ["a", "b"]
if is_str_list(xs):
print(xs[0].upper()) # 类型检查器在这里知道 xs: list[str] —— 实测运行正确
else:
# 这里检查器知道 xs 不是 list[str]
...
判别函数、解析器、HTTP 层的 payload 校验都值得用 TypeIs 重写一遍。注意与 TypeGuard 的取舍:TypeGuard 适合"你比类型系统懂得多"的场合(比如只检查结构不检查全部元素),TypeIs 适合逻辑上等价于类型测试的场合。
3. TypedDict 的 ReadOnly:配置字典的"只读视图"
3.13 把 ReadOnly 从 experimental 转正。对"会被传给多个模块的配置字典",可以精确声明哪些键不许改:
from typing import TypedDict, ReadOnly
class Config(TypedDict):
name: ReadOnly[str]
retries: int
c: Config = {"name": "api", "retries": 3}
print(c["name"]) # api —— 实测
类型检查器会拒绝 c["name"] = "x",而 retries 仍可写。库作者用它表达"这是契约字段",比整本字典 frozen=True 灵活得多。
4. TypeVar 终于可以带默认值(PEP 696)
泛型容器的"不传参数时默认什么类型"以前只能靠重载 hack,3.13 直接支持:
from typing import TypeVar
T = TypeVar("T", default=int)
class Cell[T = object]: # PEP 695 语法里用 = 默认
def __init__(self, v: T) -> None:
self.v = v
print(Cell[int](3).v) # 3 —— 实测 Cell[T = object] 正常工作、可参数化实例化
两条实测过的规则:
T.__default__、T.has_default()可以编程访问(实测T.has_default() == True);- 顺序规则:没有默认值的参数必须写在有默认值的参数前面——
class Slot[U2, U = int]合法,反序会得到TypeError: Type parameter ... without a default follows type parameter with a default(这个报错我们在 3.13.15 上实测复现)。
这对写 SDK 的同学是质变:class Client[Backend = HTTPBackend] 终于能按直觉写。
5. warnings.deprecated:标准库自带的弃用装饰器
以前要么引 deprecation 三方包,要么手写 wrapper。3.13 内置了:
import warnings
from warnings import deprecated
@deprecated("use new_func instead", category=DeprecationWarning)
def old_func(x):
return x + 1
@deprecated("Class X is legacy")
class X: ...
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
old_func(1) # 调用时告警 'use new_func instead' —— 实测
X() # 告警 'Class X is legacy' —— 实测
函数、类、方法、property 都能挂;配合 category=DeprecationWarning 的默认过滤,用户平时看不到噪音,跑测试时(-W error::DeprecationWarning)自动炸出来。
6. pathlib 补齐的最后几块
3.13 的 pathlib 一口气加了几个"早该有"的东西(实测):
from pathlib import Path
# 从 file:// URI 构造路径(解析下载链接、剪贴板路径时非常有用)
print(Path.from_uri("file:///C:/tmp/a.txt")) # C:\tmp\a.txt —— 实测
# full_match:支持 ** 的完整路径模式匹配(match 只匹配末段,full_match 匹配全路径)
assert hasattr(Path, "full_match") # 实测 3.13 存在
加上 3.12 的 Path.walk(),pathlib 对 os/shutil 的替代基本完整了。
7. itertools.batched(strict=True):分批打包的守门员
batched(3.12 加入)是处理"每 n 个一组"的最佳工具,3.13 给它补了 strict:
from itertools import batched
print(list(batched([1, 2, 3, 4, 5], 2)))
# [(1, 2), (3, 4), (5,)] —— 实测
list(batched([1, 2, 3, 4, 5], 2, strict=True))
# ValueError: batched(): incomplete batch —— 实测
print(list(batched([1, 2, 3, 4], 2, strict=True)))
# [(1, 2), (3, 4)] —— 实测
入库、分页、批量 API 调用前先用 strict 校验数据量,比事后发现"最后一批少了 1 条"便宜得多。
8. 标准库的小甜点们(都在 3.13 实测过)
import glob, math, os, itertools, hashlib
# glob 模式 -> 正则:预处理一次,批量匹配时性能起飞
print(glob.translate("data/*.csv", recursive=True))
# CPU 数的"进程视角":尊重容器/任务管理器的配额,写并发代码建议用它
print(os.process_cpu_count()) # 容器里通常是配额数而非宿主机核数
# 数学点积:一行替代 sum(a*b for ...),更快且语义清晰
print(math.sumprod([1, 2, 3], [10, 20, 30])) # 140
# 文件哈希一步到位(3.11 加入,配 3.13 的使用习惯刚好)
print(hashlib.file_digest(open("big.bin", "rb"), "sha256").hexdigest())
# 相邻配对(3.10 加入):滑动窗口的经典一行
print(list(itertools.pairwise("abcde"))) # [('a','b'), ('b','c'), ('c','d'), ('d','e')]
9. 随机数与自省的 CLI 化
3.13 给两个模块加了命令行入口(实测):
# 不用再写一行 python -c 了
python -m random 10 # 1..10 的随机整数 —— 实测输出 6
python -m random a b c # 随机选一个 —— 实测输出 a
python -m random -f 5 # 0..5 随机浮点(-i/-c/-f 见 --help)
自省:打印任意对象源码/信息(语法 module:qualname)
python -m inspect "json:JSONDecoder.decode" python -m inspect -d "json" # 显示模块的来源、加载器、缓存位置 ```
我们在干净的 CPython 上实测 -m inspect 输出函数源码与模块详情(Origin/Loader 等)完全正常。一个小坑:对 os、sys 这类内建/冻结模块会报 could not get source code——它们没有 Python 源文件可看。
10. 顺手盘一盘 3.13 的"两大实验"
- free-threaded 构建(
PEP 703):3.13 就是先装着玩的阶段,性能代价明显;到 3.14 已正式支持、单线程代价 5-10%——生产多线程场景建议直接从 3.14t 起步(见我们 3.14 那篇的实测数据); - 实验性 JIT:3.13 打了地基,3.14 官方二进制已带开关。日常业务代码暂时感知不到,可以观望。
另外 3.13 的交互式 REPL 重写(彩色、多行编辑、更好的报错)值得所有还在用裸解释器的人升级——这是零成本的幸福感。
小结
| 特性 | 一句话 | 用于 |
|---|---|---|
copy.replace |
通用版 dataclasses.replace | 不可变对象微调 |
TypeIs |
双向收窄的类型守卫 | 校验/判别函数 |
ReadOnly |
TypedDict 单键只读 | 配置契约 |
| PEP 696 | TypeVar 默认值 | 泛型 SDK |
warnings.deprecated |
标准库弃用装饰器 | API 治理 |
batched(strict=) |
分批守门员 | 批处理 |
-m random/-m inspect |
模块 CLI | 日常小工具 |
3.13 → 3.14 没有断层:3.13 的这些特性在 3.14 全部可用,而 3.14 又在其上补了模板字符串、惰性注解和子解释器。如果你还停在 3.11/3.12,一次跳到 3.14 的收益最大。
验证环境:Windows 11 x64;CPython 3.13.15(AutoClaw 自带发行版)与 CPython 3.14.0(python-build-standalone,uv 安装)。文中代码均于 2026-09-28 实际运行,输出为真实输出;
-m inspect的完整流程在标准 CPython 3.14 上验证(所测 3.13.15 为冻结标准库的发行版,内建模块无源码可显示,属该发行版特例而非 3.13 缺陷)。引自官方文档的表述(docs.python.org)以官方为准。