第13章: Python 3.10-3.13 新特性速查
按版本列出核心新特性及与 Java/Kotlin 的对比,作为持续参考。每个特性标注最低版本要求,所有 demo 代码在对应版本可直接运行。
13.1 Python 3.10 新特性
match/case 结构模式匹配 (PEP 634-636)
Java/Kotlin 对比
// Java 21+ switch 模式匹配 (JEP 406/441)
switch (obj) {
case Integer i when i > 0 -> System.out.println("正整数: " + i);
case String s -> System.out.println("字符串: " + s);
case int[] arr -> System.out.println("数组长度: " + arr.length);
case null -> System.out.println("null");
default -> System.out.println("其他");
}
// Kotlin when — 最接近 Python match 的 JVM 表达式
when (val obj = getValue()) {
is Int -> println("整数: $obj")
is String -> println("字符串: $obj")
in 1..10 -> println("1到10之间")
else -> println("其他")
}
核心差异: Python match 是结构模式匹配,不是 switch。它匹配的是数据的形状,不仅仅是值。Kotlin when 更接近值匹配,Java switch 介于两者之间。
Python demo
# [3.10+] match/case 完整模式演示
# === 1. 字面量模式 ===
def describe(value):
match value:
case 0:
return "零"
case 1 | 2 | 3: # OR 模式
return "一到三"
case "hello":
return "问候语"
case _:
return "其他"
print(describe(2)) # 一到三
print(describe("hello")) # 问候语
print(describe(42)) # 其他
# === 2. 捕获模式 ===
def process_command(cmd):
match cmd.split():
case ["quit"]:
return "退出"
case ["move", x, y]:
return f"移动到 ({x}, {y})"
case ["delete", *targets]: # *rest 捕获剩余
return f"删除: {targets}"
case _:
return "未知命令"
print(process_command("move 10 20")) # 移动到 (10, 20)
print(process_command("delete a b c")) # 删除: ['a', 'b', 'c']
# === 3. 序列模式 ===
def classify_point(point):
match point:
case (0, 0):
return "原点"
case (0, y):
return f"Y轴上, y={y}"
case (x, 0):
return f"X轴上, x={x}"
case (x, y) if x == y: # guard 子句
return f"对角线上, x=y={x}"
case (x, y):
return f"普通点 ({x}, {y})"
case _:
return "不是二维点"
print(classify_point((0, 0))) # 原点
print(classify_point((3, 3))) # 对角线上, x=y=3
print(classify_point((1, 2))) # 普通点 (1, 2)
# === 4. 映射模式 ===
def handle_event(event):
match event:
case {"type": "click", "x": x, "y": y}:
return f"点击 ({x}, {y})"
case {"type": "key", "key": key}:
return f"按键: {key}"
case {"type": t, **rest}: # **rest 捕获剩余键
return f"事件类型={t}, 额外数据={rest}"
case _:
return "未知事件"
print(handle_event({"type": "click", "x": 100, "y": 200}))
# 点击 (100, 200)
print(handle_event({"type": "key", "key": "Enter", "timestamp": 123}))
# 按键: Enter
# === 5. 类模式(解构) ===
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def where_is(point):
match point:
case Point(x=0, y=0):
return "原点"
case Point(x=0, y=y):
return f"Y轴, y={y}"
case Point(x=x, y=y):
return f"({x}, {y})"
print(where_is(Point(0, 5))) # Y轴, y=5
# === 6. 嵌套模式 ===
def process(data):
match data:
case [{"name": name, "age": age}]:
return f"单人: {name}, {age}岁"
case [{"type": "admin", "id": id}, *rest]:
return f"管理员 {id} + {len(rest)}个普通用户"
case [("start", x), ("end", y)] if y > x:
return f"区间 [{x}, {y}]"
case _:
return "不匹配"
print(process([{"name": "张三", "age": 25}]))
# 单人: 张三, 25岁
关键认知: match/case 不是 switch 的语法糖。它是基于 PEP 634 的完整模式匹配系统,支持深度解构。但 Python 没有 case null(用 case None 代替),也没有 Java 的 sealed class 模式匹配。
| 联合类型语法
Java/Kotlin 对比
// Java: 无联合类型(可以用 Object 或 overload)
// Java 21+ JEP 441: switch 的 case 可以匹配多种类型,但不是类型声明
// Kotlin: 无联合类型,用 sealed class 模拟
sealed interface Value
data class IntVal(val value: Int) : Value
data class StrVal(val value: String) : Value
Python demo
# [3.10+] 联合类型语法
# 旧写法 (3.9 及之前)
from typing import Union
def process_old(value: Union[int, str]) -> Union[int, str]:
...
# 新写法 (3.10+)
def process(value: int | str) -> int | str:
if isinstance(value, int):
return value * 2
return value.upper()
print(process(21)) # 42
print(process("hello")) # HELLO
# 可选类型简化
def find_user(user_id: int) -> User | None:
...
# 复杂联合
def handle(data: dict[str, int] | list[int] | None) -> str:
match data:
case None:
return "无数据"
case dict():
return f"字典, {len(data)}个键"
case list():
return f"列表, {len(data)}个元素"
# isinstance 也支持联合语法
if isinstance(x, int | float):
print("数值类型")
关键认知: X | Y 是语法糖,等价于 Union[X, Y],但更直观。Kotlin 没有联合类型,通常用 sealed interface 替代。Java 也没有原生联合类型。
ParamSpec 与 Concatenate
Java/Kotlin 对比
// Kotlin: reified 泛型 + inline 函数可以获取类型信息
// 但无法捕获"整个参数列表签名"并传递给另一个函数
// 这是 Python 类型系统独有的能力
Python demo
# [3.10+] ParamSpec — 捕获函数的参数签名
from typing import ParamSpec, Concatenate, Callable, TypeVar
P = ParamSpec('P') # 捕获参数签名 (*args, **kwargs)
R = TypeVar('R') # 返回值类型
# 装饰器:保留被装饰函数的完整签名
def log_calls(func: Callable[P, R]) -> Callable[P, R]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print(f"调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log_calls
def add(a: int, b: int) -> int:
return a + b
# 类型检查器知道 add 仍然是 (int, int) -> int
result: int = add(1, 2) # 类型正确
print(result) # 调用 add \n 3
# Concatenate: 在参数列表前添加参数
def with_retry(
func: Callable[Concatenate[int, P], R]
) -> Callable[Concatenate[int, P], R]:
def wrapper(retries: int, *args: P.args, **kwargs: P.kwargs) -> R:
last_exc = None
for _ in range(retries):
try:
return func(*args, **kwargs)
except Exception as e:
last_exc = e
raise last_exc # type: ignore
@with_retry
def fetch_data(url: str, timeout: int = 30) -> str:
# 注意: retries 参数由 with_retry 注入
return f"数据来自 {url}"
# 类型检查器知道: fetch_data(retries: int, url: str, timeout: int = 30) -> str
print(fetch_data(3, "https://example.com", timeout=10))
关键认知: ParamSpec 解决的是"如何把一个函数的完整参数签名作为类型变量传递"的问题。Java/Kotlin 没有等价物,因为 JVM 的泛型擦除机制不支持这种级别的签名捕获。
带括号的上下文管理器
Python demo
# [3.10+] 多个上下文管理器可以用括号换行
# 旧写法:反斜杠续行(丑陋)
with open("input.txt") as f1, \
open("output.txt", "w") as f2, \
open("log.txt", "a") as f3:
f2.write(f1.read())
f3.write("处理完成\n")
# 新写法:括号换行(清晰)
with (
open("input.txt") as f1,
open("output.txt", "w") as f2,
open("log.txt", "a") as f3,
):
f2.write(f1.read())
f3.write("处理完成\n")
Java/Kotlin 对比: Java 的 try-with-resources 天然支持多资源,不需要特殊语法。Kotlin 的 .use {} 通过链式调用处理。Python 的改进纯粹是语法层面的可读性提升。
其他 3.10 特性
# [3.10+] zip 的 strict 参数 — 长度不一致时报错
keys = ["a", "b", "c"]
values = [1, 2] # 少一个!
# 默认行为:静默截断
print(dict(zip(keys, values))) # {'a': 1, 'b': 2} — 丢失了 "c"
# strict=True:长度不一致抛 ValueError
try:
dict(zip(keys, values, strict=True))
except ValueError as e:
print(f"错误: {e}")
# 错误: zip() argument 2 is shorter
# [3.10+] int.bit_count() — 人口计数(popcount)
n = 42 # 二进制 101010
print(n.bit_count()) # 3 — 有3个1
# Java: Integer.bitCount(42)
# Kotlin: 42.countOneBits()
# [3.10+] TypeError 建议正确参数名
def connect(host: str, port: int, timeout: float = 30.0):
...
# 3.10 之前: TypeError: connect() takes from 2 to 3 positional arguments but 4 were given
# 3.10+: TypeError: connect() takes from 2 to 3 positional arguments but 4 were given
# Did you mean: connect('localhost', 8080, 30.0, timeout=5.0)?
# (当拼写错误时,Python 会猜测你可能想用的参数名)
13.2 Python 3.11 新特性
专用自适应解释器 (PEP 659)
核心原理: CPython 3.11 引入了" specializing adaptive interpreter"。字节码在执行过程中会被"快速通道"(quickening)优化,热点代码的字节码会被替换为特化版本,跳过类型检查等通用逻辑。
Java/Kotlin 对比: 类似 JVM 的 JIT 编译,但层级更浅。JVM 有解释执行 -> C1 编译 -> C2 编译的多层优化;Python 3.11 只是在解释器层面做自适应,不涉及机器码生成。
# [3.11+] 性能提升演示
# 注意: 实际提升幅度取决于代码模式,纯计算密集型提升最明显
import timeit
def sum_loop(n: int) -> int:
total = 0
for i in range(n):
total += i
return total
# 3.11 vs 3.10 典型结果:
# Python 3.10: ~35ms
# Python 3.11: ~20ms (提升约 40%)
# Python 3.12: ~18ms (进一步优化)
time = timeit.timeit(lambda: sum_loop(1_000_000), number=10)
print(f"耗时: {time:.3f}s (10次, 每次100万次循环)")
# 提升最大的场景:
# 1. 热循环中的属性访问 (obj.attr)
# 2. 热循环中的全局/内置函数调用
# 3. 热循环中的二元运算
关键认知: 平均提速 25%,但不是所有代码都受益。I/O 密集型代码几乎无提升。这是 CPython 多年来的最大性能飞跃。
精确错误位置 (PEP 657)
Python demo
# [3.11+] traceback 精确指向出错的表达式
# 3.10 的错误提示:
# Traceback (most recent call last):
# File "example.py", line 2, in <module>
# result = a + b + c + d
# NameError: name 'd' is not defined
# 3.11 的错误提示(用 ^ 标记具体位置):
# Traceback (most recent call last):
# File "example.py", line 2, in <module>
# result = a + b + c + d
# ^^^
# NameError: name 'd' is not defined
# 实际演示
def get_user(name: str) -> dict:
users = {"alice": {"age": 30}, "bob": {"age": 25}}
return users[name]
try:
# 3.11 会精确指出是哪个键不存在
user = get_user("charlie")
except KeyError:
print("3.11 会显示: KeyError: 'charlie'")
print("并指出具体是 users[name] 中的 name 导致了错误")
Java/Kotlin 对比: Java 和 Kotlin 的异常堆栈一直都有精确的行号和列号信息(通过编译器生成)。Python 3.11 终于补齐了这个短板。
ExceptionGroup 与 except* (PEP 654)
Java/Kotlin 对比: Java/Kotlin 没有等价物。JVM 的异常模型是单线程单异常的。ExceptionGroup 是 Python 为并发场景引入的全新概念——一组相关的异常被打包在一起抛出和处理。
# [3.11+] ExceptionGroup — 异常组
# === 创建异常组 ===
errors = [
ValueError("无效值: -1"),
TypeError("类型错误: str"),
ValueError("无效值: -2"),
]
# 将多个异常打包为一个组
group = ExceptionGroup("数据处理失败", errors)
print(group)
# ExceptionGroup: 数据处理失败 (2 sub-exceptions)
# ValueError: 无效值: -1
# TypeError: 类型错误: str
# ValueError: 无效值: -2
# === except* 语法 — 按类型处理子异常 ===
def handle_group(eg: ExceptionGroup):
try:
raise eg
except* ValueError as vg:
print(f"处理了 {len(vg.exceptions)} 个 ValueError")
for e in vg.exceptions:
print(f" - {e}")
except* TypeError as tg:
print(f"处理了 {len(tg.exceptions)} 个 TypeError")
for e in tg.exceptions:
print(f" - {e}")
handle_group(group)
# 处理了 2 个 ValueError
# - 无效值: -1
# - 无效值: -2
# 处理了 1 个 TypeError
# - 类型错误: str
# === 实际场景: 并发任务中收集异常 ===
import asyncio
async def task_a():
raise ValueError("任务A失败")
async def task_b():
raise TypeError("任务B失败")
async def task_c():
return "任务C成功"
async def run_all():
results = []
exceptions = []
for coro in [task_a(), task_b(), task_c()]:
try:
results.append(await coro)
except Exception as e:
exceptions.append(e)
if exceptions:
raise ExceptionGroup("部分任务失败", exceptions)
return results
# try:
# asyncio.run(run_all())
# except* ValueError:
# print("有任务值错误")
# except* TypeError:
# print("有任务类型错误")
关键认知: except* 不是 except 的替代品,而是补充。except* 只能匹配 BaseExceptionGroup 的子异常。普通的 raise ValueError(...) 仍然用 except ValueError 捕获。
TaskGroup (PEP 654)
Java/Kotlin 对比
// Kotlin coroutineScope — 结构化并发
suspend fun fetchAll(): List<Data> = coroutineScope {
val deferred1 = async { fetchFromA() }
val deferred2 = async { fetchFromB() }
listOf(deferred1.await(), deferred2.await())
// 如果任何一个失败,整个 scope 取消
}
// Java 21+ StructuredTaskScope
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Subtask<String> user = scope.fork(() -> fetchUser());
Subtask<Order> order = scope.fork(() -> fetchOrder());
scope.join();
scope.throwIfFailed();
return new Response(user.get(), order.get());
}
Python demo
# [3.11+] TaskGroup — 结构化并发
import asyncio
async def fetch_user(user_id: int) -> dict:
await asyncio.sleep(0.1)
return {"id": user_id, "name": f"User{user_id}"}
async def fetch_orders(user_id: int) -> list[dict]:
await asyncio.sleep(0.15)
return [{"id": 1, "user_id": user_id, "amount": 100}]
async def fetch_dashboard(user_id: int) -> dict:
"""使用 TaskGroup 并发获取数据"""
async with asyncio.TaskGroup() as tg:
user_task = tg.create_task(fetch_user(user_id))
orders_task = tg.create_task(fetch_orders(user_id))
# TaskGroup 退出时,所有任务都已完成
# 如果任何一个任务抛异常,其他任务被取消,异常通过 ExceptionGroup 传播
return {
"user": user_task.result(),
"orders": orders_task.result(),
}
# 运行
result = asyncio.run(fetch_dashboard(42))
print(result)
# {'user': {'id': 42, 'name': 'User42'}, 'orders': [{'id': 1, 'user_id': 42, 'amount': 100}]}
# === 错误处理 ===
async def failing_task():
await asyncio.sleep(0.05)
raise ValueError("任务失败")
async def run_with_error():
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(fetch_user(1))
tg.create_task(failing_task())
except* ValueError as eg:
print(f"捕获到 {len(eg.exceptions)} 个 ValueError")
# 其他任务已被自动取消
# asyncio.run(run_with_error())
关键认知: TaskGroup 是 Python 对结构化并发的官方支持,语义上等同于 Kotlin 的 coroutineScope 和 Java 21 的 StructuredTaskScope。核心保证:所有子任务在退出前完成,异常自动传播和取消。
tomllib (PEP 680)
Java/Kotlin 对比: Java 用 .properties 文件 + Properties 类,Kotlin 用 HOCON 或 YAML。Python 3.11 内置 TOML 解析,因为 pyproject.toml 已成为事实标准。
# [3.11+] tomllib — 内置 TOML 解析(只读)
import tomllib
# 解析 TOML 字符串
config_str = """
[database]
host = "localhost"
port = 5432
name = "myapp"
[database.pool]
min_size = 5
max_size = 20
[server]
host = "0.0.0.0"
debug = true
"""
config = tomllib.loads(config_str)
print(config["database"]["host"]) # localhost
print(config["database"]["pool"]["max_size"]) # 20
print(config["server"]["debug"]) # True
# 解析 TOML 文件
# with open("pyproject.toml", "rb") as f:
# data = tomllib.load(f)
# print(data["tool"]["pytest"]["testpaths"])
# 注意: tomllib 只能读,不能写。写入需要第三方库 tomli_w
其他 3.11 特性
# [3.11+] Self 类型 — 方法的返回值类型是自身类
from typing import Self
class Builder:
def __init__(self, name: str = ""):
self.name = name
def set_name(self, name: str) -> Self:
self.name = name
return self
def build(self) -> Self:
return self
# 类型检查器知道链式调用返回的是 Builder,不是 object
result: Builder = Builder().set_name("test").build()
# 3.11 之前需要这样写:
# from typing import TypeVar
# T = TypeVar('T', bound='Builder')
# class Builder:
# def set_name(self: T, name: str) -> T: ...
# [3.11+] TypedDict 增强 — 支持 Required/NotRequired
from typing import TypedDict, Required, NotRequired
class UserInfo(TypedDict):
name: Required[str] # 必须存在
age: NotRequired[int] # 可以省略
email: str # 默认 Required
user1: UserInfo = {"name": "张三"} # OK, age 可省略
user2: UserInfo = {"name": "李四", "age": 25} # OK
# user3: UserInfo = {} # 类型错误: 缺少 Required 字段 'name'
# [3.11+] 异步任务组别名
# asyncio.create_task_group() 是 asyncio.TaskGroup() 的别名
13.3 Python 3.12 新特性
新泛型语法 (PEP 695)
Java/Kotlin 对比
// Java 泛型声明
public <T> T first(List<T> lst) { return lst.get(0); }
// Kotlin 泛型声明
fun <T> first(lst: List<T>): T = lst.first()
// Kotlin 泛型类
class Box<T>(val value: T)
Python demo
# [3.12+] 新泛型语法 — 不再需要 from typing import TypeVar
# === 泛型函数 ===
def first[T](lst: list[T]) -> T:
"""返回列表第一个元素"""
return lst[0]
# 类型推断
result: int = first([1, 2, 3])
text: str = first(["a", "b", "c"])
# === 泛型类 ===
class Box[T]:
def __init__(self, value: T):
self.value = value
def get(self) -> T:
return self.value
def set(self, value: T) -> None:
self.value = value
int_box: Box[int] = Box(42)
str_box: Box[str] = Box("hello")
# === 泛型类型别名 ===
# 3.12 之前
# from typing import TypeVar, Generic
# T = TypeVar('T')
# Vec = list[T]
# 3.12+
type Vec[T] = list[T]
type Matrix[T] = list[Vec[T]]
type DictOf[T] = dict[str, T]
# 使用
coordinates: Vec[float] = [1.0, 2.0, 3.0]
grid: Matrix[int] = [[1, 2], [3, 4]]
config: DictOf[str] = {"host": "localhost", "port": "8080"}
# === 多类型参数 ===
def pair[F, S](first: F, second: S) -> tuple[F, S]:
return (first, second)
p: tuple[int, str] = pair(42, "hello")
# === 带约束的泛型 ===
def max_value[T: (int, float)](a: T, b: T) -> T:
"""T 必须是 int 或 float 的子类型"""
return a if a > b else b
print(max_value(3, 7)) # 7
print(max_value(3.14, 2.72)) # 3.14
# === 对比旧语法 ===
# 旧 (3.11 及之前):
# from typing import TypeVar, Generic
# T = TypeVar('T')
# class Box(Generic[T]):
# def __init__(self, value: T) -> None:
# self.value = value
关键认知: PEP 695 的泛型语法与 Kotlin 几乎一致:def name[T](...) 对应 fun <T> name(...)。这是 Python 类型系统向主流语言靠拢的重要一步。类型参数的作用域自动限定在函数/类内部,不再污染模块命名空间。
type 语句 (PEP 695)
Java/Kotlin 对比
// Kotlin 类型别名
typealias Point = Pair<Double, Double>
typealias UserMap = Map<Int, String>
typealias Predicate<T> = (T) -> Boolean
// Java 没有类型别名(只有继承来模拟)
Python demo
# [3.12+] type 语句 — 类型别名
# === 基础别名 ===
type Point = tuple[float, float]
type UserID = int
type JSON = dict[str, "JSON | list[JSON] | str | int | float | bool | None"]
origin: Point = (0.0, 0.0)
user_id: UserID = 42
# === 泛型别名 ===
type Vec[T] = list[T]
type Mapping[K, V] = dict[K, V]
type Result[T] = tuple[bool, T | None]
numbers: Vec[int] = [1, 2, 3]
lookup: Mapping[str, int] = {"a": 1, "b": 2}
success: Result[str] = (True, "hello")
# === 对比旧语法 ===
# 旧 (3.11 及之前):
# from typing import TypeAlias
# Point: TypeAlias = tuple[float, float]
# 或
# Point = tuple[float, float] # 运行时被当作赋值
# type 语句的优势:
# 1. 明确声明"这是类型别名",不是普通变量
# 2. 支持泛型参数
# 3. 运行时不会创建实际对象(延迟求值)
# === 实际应用 ===
type EventHandler = Callable[[Event], None]
type Config = dict[str, str | int | bool | None]
type Matrix[T: (int, float)] = list[list[T]]
f-string 解除限制 (PEP 701)
Python demo
# [3.12+] f-string 解除所有限制
# === 1. 可以嵌套了 ===
# 3.11 及之前: f"{f"{x}"}" — SyntaxError!
# 3.12+:
x = 42
result = f"{f"值={x}"}"
print(result) # 值=42
# 实际用途: 条件表达式
items = [1, 2, 3]
msg = f"列表{'为空' if not items else f'有{len(items)}个元素'}"
print(msg) # 列表有3个元素
# === 2. 可以包含反斜杠了 ===
# 3.11 及之前: f"{\"hello\"}" — SyntaxError!
# 3.12+:
print(f"{'换行符: \\n'}") # 换行符: \n
name = "world"
print(f"{'hello\\n' + name}") # hello\nworld
# === 3. 可以跨多行了 ===
# 3.11 及之前: f-string 不能跨行(表达式部分)
# 3.12+:
data = {"name": "张三", "age": 25, "city": "北京"}
msg = f"""
用户信息:
姓名: {data["name"]}
年龄: {data["age"]}
城市: {data["city"]}
"""
print(msg)
# 复杂表达式也可以跨行
values = [1, 2, 3, 4, 5]
threshold = 3
result = f"超过阈值的值: {
[v for v in values if v > threshold]
}"
print(result) # 超过阈值的值: [4, 5]
# === 4. 可以包含注释 ===
f"{ # 这是注释
len([1, 2, 3]) # 列表长度
}" # 3
# === 5. 同一表达式可多次引用 ===
# 3.11 及之前: 同一个表达式在 f-string 中多次出现会被多次求值
# 3.12+: 同一表达式只求值一次(性能优化 + 语义正确)
import time
f"{time.time()}, {time.time()}" # 两个时间戳可能相同(3.12+)
关键认知: 这个改动解决了 f-string 自 Python 3.6 引入以来最大的痛点。之前各种 workaround(提前赋值给变量、使用 format() 方法)都不再需要。
typing.override (PEP 698)
Java/Kotlin 对比
// Java @Override — 编译器检查是否真的覆盖了父类方法
@Override
public String toString() { return "hello"; }
// Kotlin override — 关键字,强制标记覆盖
override fun toString(): String = "hello"
Python demo
# [3.12+] typing.override — 标记方法覆盖
from typing import override
class Animal:
def speak(self) -> str:
return "..."
def move(self) -> str:
return "移动"
class Dog(Animal):
@override
def speak(self) -> str:
return "汪汪!"
# @override
# def run(self) -> str: # 类型检查器报错: Animal 没有 run 方法
# return "奔跑"
# override 是纯类型检查工具:
# - 运行时 override 就是一个空函数,什么都不做
# - 但 mypy/pyright 会在类型检查时报错:
# 1. 如果标记了 @override 但父类没有同名方法
# 2. 如果签名与父类不兼容
# 3.12 之前需要:
# from typing_extensions import override
# 或用 mypy 的 --warn-overriding-overrides 等选项
关键认知: Python 的 @override 是装饰器而非关键字,运行时无开销。Kotlin 的 override 是关键字,编译时强制检查。Java 的 @Override 是注解。三者目的相同:防止父类方法重命名后子类"静默失效"。
Per-Interpreter GIL (PEP 684)
核心原理: 每个 Python 子解释器拥有独立的 GIL,不再共享全局锁。这是迈向真正并行 Python 的关键一步。
# [3.12+] Per-Interpreter GIL — 说明性 demo
# Python 3.12 默认仍然是全局 GIL
# PEP 684 的改动在底层 C API 层面,普通 Python 代码无感知
# 但它为以下场景铺路:
# 1. 子解释器可以真正并行运行
# 2. 3.13 的自由线程实验特性依赖此基础
# 如何创建子解释器(高级用法,通常不需要)
import _interpreters
# 列出当前所有解释器
print(_interpreters.list_all()) # [0] — 主解释器 ID 为 0
# 创建新解释器
# interp_id = _interpreters.create()
# print(_interpreters.list_all()) # [0, 1]
# 注意: _interpreters 是私有模块,API 可能变化
# 普通开发者不需要直接使用
# 它的主要受益者是 Web 框架(如 mod_wsgi)和嵌入式场景
Java/Kotlin 对比: JVM 天生没有 GIL,多线程可以真正并行。Python 的 Per-Interpreter GIL 是在现有架构约束下的渐进式改进,距离 JVM 的并行能力仍有差距。
推导式内联 (PEP 709)
# [3.12+] 推导式内联 — 性能优化
import timeit
# 列表推导式不再创建独立栈帧,直接内联到当前作用域
# 速度提升约 2x
def old_way():
# 3.11: 每次推导式创建一个隐藏函数 + 栈帧
return [x * 2 for x in range(1000)]
def new_way():
# 3.12: 直接内联,无额外栈帧开销
return [x * 2 for x in range(1000)]
# 性能对比(3.12 vs 3.11)
t = timeit.timeit('[x * 2 for x in range(1000)]', number=100_000)
print(f"列表推导式: {t:.3f}s")
t = timeit.timeit('{x: x * 2 for x in range(1000)}', number=100_000)
print(f"字典推导式: {t:.3f}s")
t = timeit.timeit('(x * 2 for x in range(1000))', number=100_000)
print(f"生成器表达式: {t:.3f}s")
# 关键变化:
# 1. 推导式内不再有独立的 __name__ 和 __qualname__
# 2. locals() 在推导式内可以访问外层变量(行为更直观)
# 3. traceback 更简洁(少一层栈帧)
其他 3.12 特性
# [3.12+] 移除 distutils
# import distutils # ModuleNotFoundError!
# 替代方案:
# - setuptools (打包)
# - packaging (版本比较)
# - sysconfig (系统配置)
# [3.12+] 改进的错误提示
# import "os" # 3.12: SyntaxWarning: invalid escape sequence '\o'
# # 建议使用 r"os" 或 "os"
# [3.12+] isinstance 检查 Protocol 提速
from typing import Protocol
class Sized(Protocol):
def __len__(self) -> int: ...
class MyList:
def __len__(self) -> int:
return 42
# 3.12 中 isinstance 对 Protocol 的检查更快
# (底层优化了 __instancecheck__ 的实现)
print(isinstance(MyList(), Sized)) # True
13.4 Python 3.13 新特性
实验性自由线程 (PEP 703)
核心原理: 允许禁用 GIL,使 Python 线程可以真正并行执行。这是 Python 历史上最大的底层变更之一。
# [3.13 实验] 自由线程 — 说明性 demo
# === 如何启用 ===
# 1. 编译时: ./configure --disable-gil
# 2. 运行时: 使用 python3.13t (t = free-threaded)
# 3. 默认构建仍然是 GIL 版本
# === 代码无需修改 ===
import threading
counter = 0
lock = threading.Lock()
def increment(n: int) -> None:
global counter
for _ in range(n):
with lock:
counter += 1
# 自由线程模式下,这些线程真正并行执行
# GIL 模式下,由于 GIL,同一时刻只有一个线程运行 Python 字节码
threads = [
threading.Thread(target=increment, args=(1_000_000,))
for _ in range(4)
]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"计数器: {counter}") # 4000000
# === 注意事项 ===
# 1. 实验性功能,不建议生产使用
# 2. 部分第三方 C 扩展不兼容
# 3. 单线程性能可能下降 5-10%(因为增加了线程安全开销)
# 4. 仍需要锁来保护共享可变状态(和 Java 一样)
Java/Kotlin 对比: JVM 从第一天起就是自由线程的。Python 花了 30+ 年才走到这一步。但 Python 的挑战更大:大量 C 扩展依赖 GIL 提供的线程安全保证。
实验性 JIT 编译器 (PEP 744)
核心原理: 使用 "copy-and-patch" 技术将热点字节码编译为机器码。这是 CPython 首次内置 JIT 编译器。
# [3.13 实验] JIT 编译器 — 说明性 demo
# JIT 默认关闭,通过环境变量启用:
# PYTHON_JIT=1 python3.13 script.py
# 代码无需任何修改,JIT 对热点代码自动生效
# 适用场景:
# 1. 纯计算循环(数值计算)
# 2. 频繁调用的函数
# 3. 类型稳定的操作
# 性能预期:
# - 热点循环: 额外提速 2-10x(在 3.11 自适应解释器基础上)
# - 整体程序: 提升幅度取决于热点占比
# 与 JVM JIT 的区别:
# JVM JIT: 解释 -> C1 (客户端编译) -> C2 (服务端编译)
# 多层编译,激进优化(内联、逃逸分析、循环展开)
# Python JIT: 初期版本,仅做基本的字节码到机器码转换
# 未来可能加入更多优化
# 示例: 一个会被 JIT 加速的热循环
def fibonacci(n: int) -> int:
if n <= 1:
return n
a, b = 0, 1
for _ in range(2, n + 1):
a, b = b, a + b
return b
# 多次调用后,JIT 可能将此函数编译为机器码
for i in range(100):
fibonacci(100_000)
改进 REPL
# [3.13+] 改进的交互式解释器
# 1. 多行编辑 — 支持上下键浏览历史
# 2. 彩色输出 — 异常信息带语法高亮
# 3. 更好的粘贴支持 — 直接粘贴多行代码
# 4. history 命令 — 查看命令历史
# 5. help 命令增强
# 启动: python3.13
# >>> 1 + 1
# 2
# >>> def foo():
# ... return 42
# ...
# >>> foo()
# 42
# 注意: 3.13 的 REPL 仍然是内置的,不是 IPython
# 但功能已经大幅接近 IPython
# 对于日常调试,内置 REPL 可能够用了
warnings.deprecated (PEP 702)
Java/Kotlin 对比
// Java @Deprecated
@Deprecated(since = "21", forRemoval = true)
public void oldMethod() { }
// 编译时产生警告,可通过 @SuppressWarnings("deprecation") 抑制
// Kotlin @Deprecated
@Deprecated("使用 newMethod 代替", ReplaceWith("newMethod()"))
fun oldMethod() { }
Python demo
# [3.13+] warnings.deprecated — 标记废弃
import warnings
# === 标记函数为废弃 ===
@warnings.deprecated(
"使用 calculate_v2() 代替",
category=DeprecationWarning,
stacklevel=2,
)
def calculate_v1(x: int, y: int) -> int:
return x + y
def calculate_v2(x: int, y: int) -> int:
return x + y + 1
# 调用废弃函数会产生 DeprecationWarning
result = calculate_v1(1, 2)
# DeprecationWarning: 使用 calculate_v2() 代替
# === 标记类为废弃 ===
@warnings.deprecated("使用 NewService 代替", stacklevel=2)
class OldService:
def do_work(self) -> None:
print("工作中...")
# === 标记参数为废弃(3.14+ 计划支持) ===
# === 对比旧方式 ===
# 3.13 之前:
# def old_func():
# warnings.warn("使用 new_func 代替", DeprecationWarning, stacklevel=2)
# return new_func()
# 3.13+:
# @warnings.deprecated("使用 new_func 代替")
# def old_func():
# return new_func()
# 更声明式,与 Java @Deprecated 语义一致
typing.TypeIs (PEP 742)
Java/Kotlin 对比
// Kotlin 智能转换 (Smart Cast)
fun process(obj: Any) {
if (obj is String) { // 编译器自动将 obj 缩窄为 String
println(obj.length) // 无需手动 cast
}
}
// 自定义类型守卫 (Kotlin 1.7+)
@OptIn(ExperimentalContracts::class)
fun String.isInt(): Boolean {
contract { returns(true) implies (this@isInt is Int) }
return this.toIntOrNull() != null
}
Python demo
# [3.13+] TypeIs — 精确的类型窄化
from typing import TypeIs
# === TypeGuard vs TypeIs 的区别 ===
# TypeGuard: 返回 True 时,参数类型缩窄到目标类型
# TypeIs: 返回 True 时,参数类型缩窄到目标类型
# 返回 False 时,参数类型缩窄到 "非目标类型"
# TypeIs 更精确,是 TypeGuard 的改进版
# === 示例: 判断是否为非空字符串 ===
def is_non_empty_string(value: object) -> TypeIs[str]:
"""返回 True 表示 value 是非空字符串"""
return isinstance(value, str) and len(value) > 0
def process(value: object) -> None:
if is_non_empty_string(value):
# 类型检查器知道: value 是 str(非空)
print(f"字符串长度: {len(value)}")
print(f"大写: {value.upper()}")
else:
# 类型检查器知道: value 不是 str,或者 str 但为空
print(f"不是非空字符串: {value!r}")
process("hello") # 字符串长度: 5 \n 大写: HELLO
process("") # 不是非空字符串: ''
process(42) # 不是非空字符串: 42
# === 示例: 判断是否为正整数 ===
def is_positive_int(value: object) -> TypeIs[int]:
return isinstance(value, int) and not isinstance(value, bool) and value > 0
def handle(value: object):
if is_positive_int(value):
print(f"正整数: {value}")
# value 被缩窄为 int
else:
print(f"不是正整数: {value!r}")
# value 被缩窄为 "非 int 或 非正 int"
handle(42) # 正整数: 42
handle(-1) # 不是正整数: -1
handle("hi") # 不是正整数: 'hi'
# === 对比 TypeGuard ===
# from typing import TypeGuard
# def is_str(value: object) -> TypeGuard[str]:
# return isinstance(value, str)
#
# TypeGuard 的问题:
# if not is_str(x): # 类型检查器只知道 x 可能不是 str
# # 不知道 x 到底是什么类型
# TypeIs 解决了这个问题:
# if not is_non_empty_string(x): # x 被缩窄为 "非 str 或空 str"
关键认知: TypeIs 是对 Kotlin 智能转换的 Python 实现。Kotlin 的 is 检查自动触发智能转换,Python 需要显式声明 TypeIs 返回类型来指导类型检查器。
typing.ReadOnly (PEP 705)
Java/Kotlin 对比
// Kotlin: val vs var, List vs MutableList
val readOnlyList: List<Int> = listOf(1, 2, 3)
// readOnlyList.add(4) // 编译错误: List 没有 add 方法
val mutableList: MutableList<Int> = mutableListOf(1, 2, 3)
mutableList.add(4) // OK
// Kotlin 的 List 是协变的只读接口,MutableList 是可变接口
// 这是 Kotlin 类型系统最优秀的设计之一
// Java: 没有真正的不可变集合(只有 unmodifiable 包装)
List<Integer> list = List.of(1, 2, 3); // Java 9+ 不可变集合
// list.add(4); // UnsupportedOperationException
Python demo
# [3.13+] ReadOnly — 标记 TypedDict 的只读键
from typing import TypedDict, ReadOnly
class User(TypedDict):
id: ReadOnly[int] # 创建后不可修改
name: str # 可以修改
email: ReadOnly[str] # 创建后不可修改
scores: list[int] # 可以修改(但列表本身可变)
user: User = {"id": 1, "name": "张三", "email": "z@example.com", "scores": [90, 85]}
# 类型检查器会报错:
# user["id"] = 2 # Error: Cannot set read-only key 'id'
# user["email"] = "new" # Error: Cannot set read-only key 'email'
user["name"] = "李四" # OK
user["scores"].append(95) # OK(ReadOnly 只管键级别,不管值是否可变)
# === 对比 Kotlin ===
# Kotlin 的 val / List 是语言层面的不可变性保证
# Python 的 ReadOnly 只是类型注解,运行时不强制
# 但配合类型检查器,可以在开发阶段捕获错误
# === 嵌套 ReadOnly ===
class Config(TypedDict):
version: ReadOnly[str]
database: ReadOnly[dict[str, str]] # 整个 database 键只读
config: Config = {
"version": "1.0",
"database": {"host": "localhost", "port": "5432"}
}
# config["version"] = "2.0" # Error
# config["database"] = {} # Error
copy.replace()
Java/Kotlin 对比
// Kotlin data class copy()
data class User(val name: String, val age: Int)
val user1 = User("张三", 25)
val user2 = user1.copy(age = 26) // 只修改 age,其余保持不变
// user2 = User(name="张三", age=26)
// Java Record (Java 16+) — 没有 copy 方法
record User(String name, int age) {}
// 需要手动实现或使用 with 方法模式
Python demo
# [3.13+] copy.replace() — 不可变对象的函数式更新
from dataclasses import dataclass, replace
# === dataclass ===
@dataclass(frozen=True)
class User:
name: str
age: int
email: str = "unknown"
user1 = User(name="张三", age=25, email="z@example.com")
# replace 创建新对象,只修改指定字段
user2 = replace(user1, age=26)
print(user1) # User(name='张三', age=25, email='z@example.com')
print(user2) # User(name='张三', age=26, email='z@example.com')
print(user1 is user2) # False — 新对象
# === 也适用于非 frozen 的 dataclass ===
@dataclass
class Config:
host: str
port: int
debug: bool = False
config1 = Config(host="localhost", port=8080)
config2 = replace(config1, port=9090, debug=True)
print(config2) # Config(host='localhost', port=9090, debug=True)
# === 对比 3.13 之前 ===
# 3.12: from dataclasses import replace
# 3.13: from copy import replace — 移到标准库 copy 模块
# 同时支持更多类型(不仅是 dataclass)
# === 支持 NamedTuple ===
from typing import NamedTuple
class Point(NamedTuple):
x: float
y: float
p1 = Point(1.0, 2.0)
p2 = replace(p1, y=3.0)
print(p2) # Point(x=1.0, y=3.0)
# === Kotlin 对比 ===
# data class User(val name: String, val age: Int)
# user1.copy(age = 26)
# 语义完全一致!
关键认知: copy.replace() 与 Kotlin 的 copy() 语义完全一致。3.13 之前 replace 在 dataclasses 模块中,3.13 移到 copy 模块并扩展支持范围。
其他 3.13 特性
# [3.13+] 移除 19 个废弃模块
# 以下模块已被移除:
# - aifc, audioop, cgi, cgitb, chunk, crypt, imghdr, mailcap, nntplib,
# nis, nntplib, ossaudiodev, pipes, pwd, spwd, sunau, telnetlib,
# xdrlib, uu
# 如果代码依赖这些模块,升级前需要找到替代方案
# [3.13+] locals() 语义明确化
# 在函数中,locals() 返回的是快照,修改它不再影响局部变量
def example():
x = 1
locals()["x"] = 42 # 不会修改 x!
print(x) # 1(不是 42)
# 3.13 明确: locals() 返回的是只读快照
# 之前的行为是未定义的(CPython 实现细节)
# [3.13+] iOS/Android 支持 (tier 3)
# Python 3.13 可以在 iOS 和 Android 上运行(tier 3 平台)
# 意味着: 可以编译运行,但不保证所有功能正常
# 适合: 移动端脚本、自动化测试、教育用途
# 不适合: 生产级移动应用(性能和兼容性不够)
版本迁移建议
什么时候升级
| 场景 | 推荐版本 | 理由 |
|---|---|---|
| 新项目 | 3.12+ | 新泛型语法、f-string 解除限制、性能提升 |
| 现有项目维护 | 升级到 3.11 | 零语法破坏、纯性能提升 25%+ |
| 需要自由线程 | 3.13t (实验) | 禁用 GIL,CPU 密集型多线程 |
| 长期稳定 | 3.11 | 最稳定的现代版本,生态兼容性最好 |
| 追求最新特性 | 3.13 | JIT、自由线程、改进 REPL |
兼容性注意事项
# === 3.10 升级检查清单 ===
# 1. match/case 是新关键字,确保没有变量名冲突
# 2. | 联合类型语法需要更新类型注解
# 3. 检查第三方库兼容性(大多数 3.9+ 的库已兼容)
# === 3.11 升级检查清单 ===
# 1. 异常处理: 如果使用了自定义异常层级,了解 ExceptionGroup
# 2. tomllib 替代 toml/tomli(只读场景)
# 3. 性能提升通常是透明的,但某些 C 扩展可能需要更新
# === 3.12 升级检查清单 ===
# 1. distutils 已移除 — 检查 setup.py 依赖
# 2. 新泛型语法是可选的,旧语法仍然有效
# 3. f-string 行为变化: 同一表达式多次引用只求值一次
# 4. 推导式不再创建独立栈帧 — 如果依赖 __name__ 会受影响
# === 3.13 升级检查清单 ===
# 1. 19 个废弃模块已移除 — 检查 import
# 2. locals() 行为明确化 — 如果依赖修改 locals() 的副作用
# 3. 自由线程和 JIT 是实验性的 — 不要在生产中默认启用
# 4. copy.replace() 从 dataclasses 移到 copy — 更新 import
废弃 API 清单
Python 3.10 废弃:
- distutils (3.12 移除)
- asyncio.get_event_loop() 在无运行循环时的隐式创建
- importlib.abc.MetaPathFinder.find_module()
Python 3.11 废弃:
- tomllib 替代第三方 toml 库(只读场景)
- typing.TypedDict 的旧式声明语法
- asyncore, asynchat (3.12 移除)
Python 3.12 移除:
- distutils (完全移除)
- asynchat, asyncore, smtpd
- imp 模块 (早已废弃)
- aifc, audioop, cgi, chunk, crypt, imghdr, mailcap, nntplib,
nis, ossaudiodev, pipes, pwd, spwd, sunau, telnetlib,
xdrlib, uu
Python 3.13 移除:
- 上述 19 个模块 (从 3.12 的废弃列表)
- py_compile.compile() 的 'legacy' 模式
- typing.TypeAlias (使用 type 语句代替)
- idlelib 中的部分旧功能
持续废弃中 (未来版本移除):
- datetime.utcnow() → datetime.now(timezone.utc)
- asyncio.get_event_loop() → asyncio.get_running_loop()
- pkgutil.get_loader() → importlib.util.find_spec()
- typing.TypedDict 的非类语法
快速参考: 各版本核心特性一览
| 特性 | 3.10 | 3.11 | 3.12 | 3.13 |
|---|---|---|---|---|
| match/case | Y | Y | Y | Y |
| X | Y 联合类型 | Y | Y | Y | Y |
| ParamSpec | Y | Y | Y | Y |
| 自适应解释器 (25%+) | Y | Y | Y | |
| 精确错误位置 | Y | Y | Y | |
| ExceptionGroup / except* | Y | Y | Y | |
| TaskGroup | Y | Y | Y | |
| tomllib | Y | Y | Y | |
| Self 类型 | Y | Y | Y | |
| 新泛型语法 def[T] | Y | Y | ||
| type 别名语句 | Y | Y | ||
| f-string 解除限制 | Y | Y | ||
| @override | Y | Y | ||
| 推导式内联 (2x) | Y | Y | ||
| 自由线程 (实验) | Y | |||
| JIT 编译器 (实验) | Y | |||
| warnings.deprecated | Y | |||
| TypeIs | Y | |||
| ReadOnly | Y | |||
| copy.replace() | Y | |||
| 改进 REPL | Y |