Course Builder个人学习工作台 · 本地模式
Modern Python:面向有经验开发者的回归路线

现代语法:字典合并、walrus 与 match

目标:能够在真实数据处理中判断何时使用字典合并、赋值表达式和结构化模式匹配,而不是把它们当作更短的旧语法。

现代语法的价值不在于减少行数,而在于让一个常见意图更直接。

  • base | overrides 创建新字典,右侧同名键覆盖左侧;base |= overrides 原地更新。
  • name := expression 在表达式中保存结果,适合“计算一次、判断并复用”,不适合把多步状态藏进条件。
  • match/case 匹配的是数据结构,可解构 mapping、sequence 和 class;它不只是 switch。

完整示例:解析 provider 响应

保存为 syntax_lab.py:

from __future__ import annotations

from dataclasses import dataclass


@dataclass(frozen=True)
class Success:
    text: str
    tokens: int


@dataclass(frozen=True)
class Failure:
    code: int
    message: str


def parse_response(value: object) -> Success | Failure:
    match value:
        case {"status": "ok", "data": {"text": str(text), "tokens": int(tokens)}}:
            return Success(text=text, tokens=tokens)
        case {"status": "error", "code": int(code), "message": str(message)}:
            return Failure(code=code, message=message)
        case ["stream", str(first), *rest] if first:
            joined = "".join([first, *(str(part) for part in rest)])
            return Success(text=joined, tokens=0)
        case _:
            raise ValueError("unsupported response shape")


def apply_defaults(payload: dict[str, object]) -> dict[str, object]:
    defaults: dict[str, object] = {"language": "zh", "priority": 0}
    return defaults | payload


if __name__ == "__main__":
    parsed = parse_response(
        {"status": "ok", "data": {"text": "hello", "tokens": 3}}
    )
    config = apply_defaults({"priority": 5})
    print(parsed)
    print(config)

运行:

uv run python syntax_lab.py

预期输出:

Success(text='hello', tokens=3)
{'language': 'zh', 'priority': 5}

mapping pattern 只要求所列键存在,额外键默认不会让匹配失败。str(text) 和 int(tokens) 是 class pattern:检查类型并绑定完整值。guard if first 只在 sequence pattern 成功后执行。

walrus 的适用边界

适合:

if (cleaned := raw.strip()):
    messages.append(cleaned)

raw.strip() 只计算一次,cleaned 紧邻其使用位置。不适合:

# 不要把多个状态变化压进一条难读条件
# if (item := next_item()) and (score := grade(item)) > limit and update(score):

一旦条件同时产生多个后续状态,普通赋值与多个 if 更便于调试。

引导实验:观察 mapping 的额外键

给成功响应增加 "request_id": "r1",运行 parser。它仍会成功。若你需要拒绝未知键,match 不是完整 schema validator;应在 Pydantic 或显式检查中表达该规则。

接着把 tokens 改成 True。由于 bool 是 int 的子类,int(tokens) 会匹配。这是 Python 对象模型的真实结果。若业务上 bool 不合法,应加 guard:

case {"tokens": int(tokens)} if not isinstance(tokens, bool):

独立练习

新增 {"status": "retry", "after": number} 分支,要求 after > 0,返回 Failure(429, ...)。其他非正值仍应进入默认错误。

参考实现
case {"status": "retry", "after": int(seconds)} if seconds > 0:
    return Failure(code=429, message=f"retry after {seconds}s")

验收:

assert parse_response({"status": "retry", "after": 2}) == Failure(429, "retry after 2s")
try:
    parse_response({"status": "retry", "after": 0})
except ValueError:
    pass
else:
    raise AssertionError("zero delay must be rejected")

参考资料