
简介Python 3.11.0官方What’s New中文版文档完整PDF面向需要跟进新版语言特性的Python开发者、运维人员及编程爱好者帮助读者快速掌握3.11版本的重要更新与潜在变化。资源为单个PDF文件A4版式排版完整压缩包总大小仅18.45MB既适合电脑阅读也便于打印后作为随查手册使用。目前已有754人学习浏览是Python中文学习资料中较受关注的在线版本更新指南。PDF内容覆盖PEP 657细粒度错误定位、PEP 654异常组与except*、PEP 678异常注释等核心新特性并详解变长泛型、TypedDict必需项标记、Self类型、任意字面字符串类型、数据类转换等类型提示增强同时汇总asyncio、contextlib、dataclasses、datetime、enum、fractions等多个模块的改进。全书按官方文档结构分为新特性、类型提示、其他语言变化、模块改进等章节便于读者按需查阅适合作为Python 3.11日常开发与系统学习中的结构化速查手册。1. Python 3.11.0 中文教程一份能直接照着排查报错的官方更新手册我接手过一个从 3.8 升到 3.11 的内部服务第一晚就被一个报错卡住AttributeError: NoneType object has no attribute x。旧版解释器只告诉我第 6 行出了问题而 3.11 直接画出是point_1.x还是point_2.x挂了。这份 Python 课程资源不是语法速查表也不是某个软件/插件的配套说明而是官方 Whats New in Python 3.11.0 的中文完整版A4 PDF 排版从细粒度错误定位、异常组、类型提示新特性到 Faster CPython 的变化全在里面。它适合三类人准备从 3.8/3.10 升级的从业人员、在 CI 里被模糊报错折磨的排查者、想尝鲜类型系统的学习者。先读哪章取决于你现在被哪个问题卡住。2. 把 PEP 657 用起来精确错误定位与异常组的落地写法2.1 先看懂 traceback 里那串 ^^^^PEP 657 到底改了啥Python 3.11 之前traceback 只能定位到出错的行。遇到return abs(point_1.x - point_2.x) abs(point_1.y - point_2.y)这种复合表达式报错后你得自己猜是哪个子表达式返回了 None。PEP 657 做的事情是给每个字节码指令额外记录源码的行列范围解释器打印异常时把箭头精确画到「小表达式」上。代价是 code object 多存一份位置信息.pyc文件和内存占用会略微上涨。下面这段代码是我在某模拟项目 X 里复现官方文档例子时写的class Point: def __init__(self, xNone, yNone): self.x x self.y y p1 None # 人为制造 None模拟脏数据 p2 Point(x3, y4) def manhattan_distance(point_1, point_2): return abs(point_1.x - point_2.x) abs(point_1.y - point_2.y) print(manhattan_distance(p1, p2))用 Python 3.11 运行traceback 会指向point_1.x而不是整行return abs(...)。原理是解释器把出错位置细化到属性访问的起点和终点point_1.x的列区间被单独标记。换到 3.10 只会得到File distance.py, line 6后面跟着整行源码看不出point_1是罪魁祸首。位置信息还能通过 API 拿来做调试工具。co_positions()是 3.11 给 code object 新增的方法逐条返回指令对应的源码位置def add(a, b): return a b * 2 for pos in add.__code__.co_positions(): print(pos)输出是四元组(起始行, 起始列, 结束行, 结束列)。注意有些指令的列值可能为None尤其是从优化后字节码推导不出位置时。我一般用它在自定义 profiler 里画源码高亮效果类似网页端错误定位。关闭这个特性有官方开关-X no_debug_ranges或环境变量PYTHONNODEBUGRANGES1。CI 机器上日志噪音太大时可以用但本地开发建议保留它是 3.11 最值的调试红利。2.2 except* 与 ExceptionGroup一次收下所有异常并发任务和批量校验里最常见的尴尬是多个任务同时失败只能抛出一个异常其余的被吞掉。PEP 654 引入ExceptionGroup和BaseExceptionGroup把多个无关异常打包抛出一个对象配套的except*新语法负责按类型把组里的子异常拆出来分别处理。下面这个例子模拟批量数据校验def check_many(items): errors [] for it in items: if not isinstance(it, int): errors.append(TypeError(fnot int: {it!r})) if it 0: errors.append(ValueError(fnegative: {it})) if errors: raise ExceptionGroup(check_many failed, errors) try: check_many([1, -2, 3, -4]) except* ValueError as eg: print(value errors:, eg.exceptions) except* TypeError as eg: print(type errors:, eg.exceptions)执行后两个 except* 块会分别拿到各自的子异常集合。eg.exceptions是元组里面是匹配到的子异常。没被任何 except* 匹配到的子异常会在当前 try 结束后重新组合成新的 ExceptionGroup 抛出去避免异常被静默弄丢。有一点必须说清楚except*和普通except的匹配逻辑不能混着理解。普通except ExceptionGroup会把整个组当成单一异常接住内部子异常不会自动展开。如果需要手动拆分常见做法是用eg.split(TypeError)它返回(匹配组, 剩余组)两个对象。我踩过的坑是刚上线时把except* ValueError拼错成except ValueError子异常全被组对象包住后面的告警逻辑完全没跑——这会引出一整类排查事故后面避坑章会细说。2.3 add_note给异常补上下文PEP 678 给BaseException增加了add_note()方法。用途一句话在异常已经产生、但还没抛到最终处理者之前往异常对象上追加上下文信息这些信息会出现在 traceback 末尾。def parse_config(path): try: with open(path, encodingutf-8) as f: return json.load(f) except json.JSONDecodeError as exc: exc.add_note(f解析失败的文件: {path}) exc.add_note(f文件大小: {os.path.getsize(path)} 字节) raise第一次 add_note 记录的是「读文件时拿不到的信息」比如完整路径第二次记录的是「捕获时才知道的信息」比如文件大小。多次调用会按顺序追加3.11 内部存在exc.__notes__列表里。注意__notes__是文档明确提到的属性但它是双下划线成员外部日志系统不会自动读取整合时得自己拿。这套组合拳的实际价值是底层模块抛异常时不用费劲构造长消息字符串把结构化信息塞进 notes由最顶层的错误处理页统一展示。比起把路径、参数、上下文全拼进 messagenotes 让异常本身保持短小排查时信息量反而更大。3. 类型提示这波更新从 TypeVarTuple 到 Self 的五个可运行例子3.1 变长泛型 TypeVarTupleshape 怎么标PEP 484 时代的TypeVar只能占一个类型参数位张量库想表达「形状可变」就得靠注释或字符串。PEP 646 给的TypeVarTuple可以占据任意多个类型参数位典型场景是数值计算库的数组形状标注。from typing import Generic, TypeVarTuple, Tuple Shape TypeVarTuple(Shape) class Array(Generic[*Shape]): def __init__(self, shape: Tuple[*Shape]): self.shape shape vec Array(shape(3,)) mat Array(shape(2, 3)) tensor Array(shape(2, 3, 4))*Shape的展开语法让Array能同时接受一维、二维、三维的标注。静态检查器看到Array[tuple[int, int]]时能推断出这是二维数组形状相关的 bug 在编码阶段就能暴露。运行时别对这个机制期望太高typing.get_args()在某些未绑定场景下会返回TypeVarTuple对象本身不是可用的类型。我一般只在接口签名层用它真正的 shape 校验还是靠运行时逻辑。3.2 Required 与 NotRequiredTypedDict 的必填键可控制了TypedDict 过去只能通过totalFalse一刀切要么全部必填要么全部可选。PEP 655 把控制粒度降到单个 key。from typing import TypedDict, Required, NotRequired class Movie(TypedDict): title: str year: NotRequired[int] m1: Movie {title: Black Panther, year: 2018} # OK m2: Movie {title: Star Wars} # OK m3: Movie {year: 2022} # 类型检查报错默认totalTrue没标 NotRequired 的字段仍然必填。反向写法也支持class Movie(TypedDict, totalFalse)时用year: int表示可选用title: Required[str]表示必填。两种风格按团队习惯选一种即可混用容易让阅读者困惑。兼容性提醒Python 3.9 和 3.10 没有这两个名字需要typing_extensions里的同名实现。我维护的某跨平台系统为了兼容旧环境统一从typing_extensions导入3.11 迁移时再切回标准库。3.3 Self 类型替代 TypeVar 样板代码PEP 673 之前标注「返回自身类型」得写TypeVar加 bound样板代码很啰嗦。Self 让返回类型直白表达。from typing import Self class MyInt: classmethod def fromhex(cls, s: str) - Self: return cls(int(s, 16)) class SubInt(MyInt): pass print(SubInt.fromhex(ff))fromhex返回 Self 后子类调用返回的是SubInt而不是MyInt链式调用和构造器复用都能保留准确类型。3.11 里 Self 可以直接出现在注解位置不需要字符串引用也不需要from __future__ import annotations。常见误用是把 Self 放在参数位置。语法上允许但语义上容易让人误会不如直接用类名。另外在运行时用get_type_hints解析含 Self 的注解不同实现表现有差异调试时别拿裸__annotations__硬读。3.4 LiteralString 与 dataclass_transformPEP 675 的LiteralString是给敏感接口设计用的它只接受字面量字符串以及由字面量拼接出来的字符串不接受任意变量内容。from typing import LiteralString def run_sql(sql: LiteralString) - None: # 类型检查器只允许字面量字符串进入 print(sql) run_sql(SELECT * FROM users) # OK run_sql(SELECT * FROM table_name) # 类型检查报错必须强调LiteralString只在静态检查层面生效运行时没有任何拦截。把它当安全机制是危险的它只是给 API 消费者一个明确契约真正过滤输入还得靠参数化查询。dataclass_transform解决的是另一类需求自定义类工厂被类型检查器当成 dataclass 处理。from typing import dataclass_transform dataclass_transform() def model(cls, **kwargs): return type(cls.__name__, (cls,), kwargs) model class User: name: str age: int装饰器标记后类型检查器会按 dataclass 的规则推导User的__init__、字段类型等信息。运行时它几乎不做任何事真正生成代码的逻辑还在你自己的工厂里。ORM 基类、配置类工厂都适合用。4. 性能与解释器变化从 1.25 倍提速到 py.exe 启动器改了什么4.1 Faster CPython 快在哪启动与运行时官方对 3.11 的定位很明确比 3.10 快 10% 到 60%标准基准测试平均约 1.25 倍。不是每个项目都能感受到IO 密集型应用可能毫无变化CPU 密集的递归、循环、属性访问密集代码收益最大。提速来源主要有三块自适应专用化解释器在运行时统计指令执行情况把通用字节码替换成针对具体类型的专用版本、更快的字节码求值循环、更快的函数调用机制。注意它仍然是解释执行不是 JIT 编译成机器码。想验证自己环境有没有吃到红利最直接是同一个函数在两个版本下跑时间python3.11 - PY from timeit import timeit def fib(n): return n if n 2 else fib(n - 1) fib(n - 2) print(timeit(lambda: fib(30), number3)) PY再把命令里的python3.11换成 3.10 跑一遍。number3是为了取多次结果看波动单跑一次受系统调度影响太大。我用这套方法在模拟项目 X 上实测递归场景约快 35%但换了另一个字符串处理任务只快 8%结论是别信宣传数字跑自己业务代码才有意义。4.2 -P 与 PYTHONSAFEPATHsys.path 安全开关3.11 新增的-P参数会把「自动把脚本所在目录加入 sys.path」这个行为关掉。默认情况下执行python app.py会把app.py所在目录塞进sys.path[0]这给恶意脚本制造了机会当前目录下放一个os.py应用导入 os 时可能被劫持。python -c import sys; print(sys.path[:2]) python -P -c import sys; print(sys.path[:2])第一行能看到脚本目录出现在路径里第二行那个位置变成其他标准路径。设置环境变量PYTHONSAFEPATH1效果相同。-P和已有的-I隔离模式不同-I是更彻底的隔离同时关掉用户 site-packages、环境变量等-P只针对路径自动拼接。我的习惯是从网上下载的脚本、给第三方跑的一次性脚本一律python -P script.py成本几乎为零。4.3 py.exe 启动器与字节码变化Windows 上py.exe启动器这次改动不小核心是支持 PEP 514 的 company/tag 语法。原来-3.11这种写法只能选 python.org 的 PythonCore 发行版新语法-V:company/tag可以选别的发行版。py.exe 参数含义-V:3.11选择 tag 为 3.11 的最佳发行版-V:/3.11省略 company效果同上-V:OtherPython/选择某公司注册的 OtherPython 发行版-3.11-64旧写法其中-64现在指“非 32 位”注意旧参数只匹配 PythonCore-32后缀对应 32 位运行时-64不再特指 x86-64 架构。多版本管理变得方便但团队里有人用旧参数踩坑的话升级后要统一检查 CI 脚本。字节码层面3.11 新增了一批 opcode替换和删除了一批旧的导致 3.10 生成的__pycache__全部作废。这是正常现象首次运行重新生成.pyc会慢一点点。想看重新编译是否完成可以观察缓存文件时间戳find . -name *.pyc -newer app.py | head4.4 标准库模块变化怎么查这份文档的目录列出 28 个以上被改进的标准库模块包括asyncio、contextlib、dataclasses、datetime、enum、fractions、functools、hashlib、logging、math、os、pathlib、re、shutil、socket、sqlite3、threading、time、tkinter、traceback、typing、unittest、venv、warnings、zipfile等。PDF 的优势就在这按模块名翻目录能直接定位到具体章节比网页逐页滚动高效。我建议把「我项目里 import 了哪些模块」列一张清单挨个在文档目录里查。查不到细节的模块别慌目录里出现的名字至少说明有行为变化值得去对应章节看变更说明。5. 升级 3.11 的避坑清单五个我实际踩过的问题与排查方法5.1 精确报错让 CI 日志变长现象升级后 CI 日志里每个报错后面多了几行^^^箭头个别日志平台把长行截断反而看不全关键信息。原因PEP 657 默认开启解释器为每个指令保存列位置traceback 里会精确标出子表达式范围。解决CI 脚本里启动 Python 时加-X no_debug_ranges或者设置环境变量PYTHONNODEBUGRANGES1。本地开发保留默认值只关 CI。磁盘空间敏感的部署环境也建议关掉.pyc文件会略大一点。5.2 except 吞掉 ExceptionGroup现象代码里写except ValueError想接住组里的子异常结果一个都没打印但程序又没崩溃。原因ExceptionGroup 是一个对象普通except把整个组接住内部子异常不会自动展开eg.exceptions没被处理异常被静默吞掉。解决处理异常组必须用except*。如果需要手动拆分组用eg.split(TypeError)得到(匹配组, 剩余组)剩余组要显式记录或抛出别让它在函数末尾静默消失。5.3 add_note 在日志系统里消失现象代码里加了exc.add_note(...)本地终端能看到 notes但日志平台只显示异常消息和堆栈notes 不见了。原因多数日志格式器只格式化str(exc)和 traceback不会读取exc.__notes__属性。解决自定义日志 formatter在格式化异常时把getattr(exc, __notes__, [])拼接进消息。注意__notes__是双下划线属性在文档中属于官方实现的公开属性工具链可以直接读。5.4 第三方 C 扩展需要重新编译现象升级后 import 某个库报ImportError提示不是有效的 Win32 应用程序或者编译安装时找不到Python.h里的函数。原因3.11 更新了 C API部分宏改成 static inline 函数移除了Py_UNICODE编码 API旧扩展必须针对新版本重新编译。解决优先找带cp311标记的 wheel没有的就暂时把那个库留在 3.10 环境等上游适配。业务代码侧先做抽象隔离把扩展依赖的模块限定在一个适配层里避免全仓库被拖住。5.5 PEP 563 不再是推荐的默认策略现象新项目沿用from __future__ import annotations配合 Self 和 LiteralString 出现类型检查器不识别、运行时注解奇怪报错。原因PEP 563 的延迟求值方案在 3.11 版本明确不再被视为未来默认方向字符串化注解在部分工具链里反而制造问题。解决新代码直接写运行时注解3.11 对 Self、TypedDict 原生支持不需要 future import。只有为了兼容 3.9 和 3.10 才保留并且用typing.get_type_hints读取注解不要访问裸__annotations__。6. 验证你的解释器是否真的吃到 3.11 的红利两条命令加一个习惯第一条命令验证精确报错开关是否打开python -c import sys; print(sys.version); print(sys._xoptions)输出字典里如果出现{no_debug_ranges: True}说明位置信息被关了输出{}就说明 PEP 657 在默认生效。第二条命令把废弃警告当成错误跑一遍测试python -X dev -W error::DeprecationWarning -m unittest discover -s tests-X dev开启开发模式-W error::DeprecationWarning把所有 DeprecationWarning 升级成异常。升级大版本后最怕的是「代码还在跑但底层已经在废弃边缘」这条命令能把隐患提前炸出来。验证完开关和警告还有一个我坚持的习惯把升级后第一个报错的 traceback 截图或复制下来和 3.10 的同场景报错做一次并排对比。看箭头是否指向真正的错误表达式看 note 是否补充了当时拿不到的上下文。有一次我在某跨平台系统里排查一个深层字典取值的 TypeError3.10 下花了二十分钟加日志定位3.11 下箭头直接指到response[a][b][c][user]这串下标里None出现的位置差距就是这么大。从那以后我每次把项目迁移到新的小版本都强制走一遍「开 -X dev、把 DeprecationWarning 升级成错误、跑测试、抽查 traceback」四步省下的排查时间比多读两遍文档值。希望帮到你。本文还有配套的精品资源点击获取