python从入门到精通-第13章: Python 3.10-3.13 新特性速查

3 阅读27分钟

第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 之前 replacedataclasses 模块中,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.13JIT、自由线程、改进 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.103.113.123.13
match/caseYYYY
X | Y 联合类型YYYY
ParamSpecYYYY
自适应解释器 (25%+)YYY
精确错误位置YYY
ExceptionGroup / except*YYY
TaskGroupYYY
tomllibYYY
Self 类型YYY
新泛型语法 def[T]YY
type 别名语句YY
f-string 解除限制YY
@overrideYY
推导式内联 (2x)YY
自由线程 (实验)Y
JIT 编译器 (实验)Y
warnings.deprecatedY
TypeIsY
ReadOnlyY
copy.replace()Y
改进 REPLY